导读部分
-------------------------------------------------------------------------------------------------------------
C#仿QQ皮肤-实现原理系列文章导航 最新版源码下载

http://www.sufeinet.com/thread-2-1-1.html   

 

    这个控件是在系统控件ComboBox的基础之上来实现的,我们先来看一下实现后的效果

C#仿QQ皮肤-ComboBox 控件实现_字符串                           C#仿QQ皮肤-ComboBox 控件实现_html_02         

 

 

    这是实现 后的效果,与普通的不同之处有,颜色有所变化,倒三角有所变化,再就是有一个鼠标跟随的效果。下面我来看看是怎么样实现的吧。

  我们第一步要打开我们的项目,然后右击项目,新添加一个Component组件。然后继承一下Combox类

 代码如下

 public class ComboBox : System.Windows.Forms.ComboBox

下面我们先定义几个变量

  //鼠标Move事件时图片
        private Image _mouseMoveImage = null;

        //鼠标mouseDown事件时图片
        private Image _mouseDownImage = null;
        //
        private Image _normalImage = null;

下面我看看再在构造方法里设置一下些固定的属性

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码

        public ComboBox()
            : base()
        {
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            //设置为手动绘制
            this.DrawMode = DrawMode.OwnerDrawFixed;
            //设置固定的DropDownList样式
            this.DropDownStyle = ComboBoxStyle.DropDownList;
            this.UpdateStyles();
        }

 

 

 为了让控件里的项目大小一至方便绘制和添加效果我们要重写一下OnCreateControl方法来固定一下Item的大小

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码
  protected override void OnCreateControl()
        {
            base.OnCreateControl();
            if (!DesignMode && this.Items.Count != 0)
            {
                this.DropDownHeight = this.Items.Count * 17;
            }
            ResetBitmap();
        }

 

ResetBitmap()方法就是用来设置不同情况下的颜色的

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码
 public void ResetBitmap()
        {
            this.NormalImage = Shared.NomalDrawButton;
            this.MouseDownImage = Shared.MouseDownDrawButton;
            this.MouseMoveImage = Shared.MouseMoveDrawButton;
        }

 

 

Shared类的方法和属性请大家参考源代码里的 ,源代码的下载在  http://www.sufeinet.com/thread-2-1-1.html   

   这里都有提供。

下面还是老规矩重写一下WndProc方法吧,

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码
  protected override void WndProc(ref Message m)
        {
            IntPtr hDC = IntPtr.Zero;
            Graphics gdc = null;

            switch (m.Msg)
            {
                case 133:
                    hDC = Win32.GetWindowDC(m.HWnd);
                    gdc = Graphics.FromHdc(hDC);
                    Win32.SendMessage(this.Handle, WM_ERASEBKGND, hDC.ToInt32(), 0);
                    SendPrintClientMsg();
                    Win32.SendMessage(this.Handle, WM_PAINT, 0, 0);
                    OverrideControlBorder(gdc);
                    m.Result = (IntPtr)1;    // indicate msg has been processed
                    Win32.ReleaseDC(m.HWnd, hDC);
                    gdc.Dispose();
                    break;
                case WM_PAINT:
                    base.WndProc(ref m);
                    hDC = Win32.GetWindowDC(m.HWnd);
                    gdc = Graphics.FromHdc(hDC);

                    OverrideDropDown(gdc);
                    OverrideControlBorder(gdc);
                    Win32.ReleaseDC(m.HWnd, hDC);
                    gdc.Dispose();
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            } 
        }

 

 

因为我们的图片颜色是手动绘制上去的所以我们应该在OverrideDropDown方法里进行绘制,也就是说当我们出现下拉列表的时候要绘制一下效果。

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码

        private void OverrideDropDown(Graphics g)
        {
            if (DesignMode) return;

            Rectangle rect = new Rectangle(this.Width - DropDownButtonWidth, 0, DropDownButtonWidth, this.Height);

            g.FillRectangle(new SolidBrush(Color.White), rect);

            if (this.Enabled)
            {
                if (_mouseEnter)
                {
                    g.DrawImage(this.MouseMoveImage, new Rectangle(this.Width - 20, 3, 16, 16));
                }
                else
                {
                    g.DrawImage(this.NormalImage, new Rectangle(this.Width - 20, 3, 16, 16));
                }
            }
            else
            {
                g.DrawImage(Shared.NotEnableDrawButton, new Rectangle(this.Width - 20, 3, 16, 16));
            }
        }

 

根据鼠标的状态不同绘制出不同的效果,大家看一下If语句 就明白 了

还有一个更重的方法就是OnDrawItem方法这是在加载时绘制一下初始化的效果

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            Graphics g = e.Graphics;
            //绘制区域
            Rectangle r = e.Bounds;

            Font fn = null;
            if (e.Index >= 0)
            {
                if (e.State == DrawItemState.None)
                {
                    //设置字体、字符串格式、对齐方式
                    fn = e.Font;
                    string s = this.Items[e.Index].ToString ();
                    StringFormat sf = new StringFormat();
                    sf.Alignment = StringAlignment.Near;
                    //根据不同的状态用不同的颜色表示
                    if (e.State == (DrawItemState.NoAccelerator | DrawItemState.NoFocusRect))
                    {
                        e.Graphics.FillRectangle(new SolidBrush(Color.Red), r);
                        e.Graphics.DrawString(s, fn, new SolidBrush(Color.Black), r, sf);
                        e.DrawFocusRectangle();
                    }
                    else
                    {
                        e.Graphics.FillRectangle(new SolidBrush(Color.White), r);
                        e.Graphics.DrawString(s, fn, new SolidBrush(Shared.FontColor), r, sf);
                        e.DrawFocusRectangle();
                    }
                }
                else
                {
                    fn = e.Font;
                    StringFormat sf = new StringFormat();
                    sf.Alignment = StringAlignment.Near;
                    string s = this.Items[e.Index].ToString ();
                    e.Graphics.FillRectangle(new SolidBrush(Shared.ControlBackColor), r);
                    e.Graphics.DrawString(s, fn, new SolidBrush(Shared.FontColor), r, sf);
                    
                }
            }
        }

 

 

有了这些我们现在要做的就是在OnMouseEnter事件OnMouseLeave事件里处理一下绘制的效果和参数就算是完工了

我们一起来看看这两个事件的处理方法吧

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码
  protected override void OnMouseEnter(EventArgs e)
        {
            _mouseEnter = true;

            IntPtr hDC = IntPtr.Zero;
            Graphics gdc = null;
            hDC = Win32.GetWindowDC(this.Handle);
            gdc = Graphics.FromHdc(hDC);

            gdc.DrawImage(Shared.MouseMoveDrawButton, new Rectangle(this.Width - 20, 3, 16, 16));

            Win32.ReleaseDC(this.Handle, hDC);
            gdc.Dispose();

            base.OnMouseEnter(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            _mouseEnter = false;
            IntPtr hDC = IntPtr.Zero;
            Graphics gdc = null;
            hDC = Win32.GetWindowDC(this.Handle);
            gdc = Graphics.FromHdc(hDC);

            gdc.DrawImage(Shared.NomalDrawButton, new Rectangle(this.Width - 20, 3, 16, 16));

            Win32.ReleaseDC(this.Handle, hDC);
            base.OnMouseLeave(e);
        }

 

到这里其实就可以完工了,其它的方法和事件都是一相辅助,等下可以看一下全部代码。

具体的使用方法和系统的Combox控件没有任何的分别。直接绑定数据就OK了。

为了方便以后的改动我把三个效果时产生的图片都定义成属性大家使用起来会方便一些

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码
  public Image MouseMoveImage
        {
            get
            {
                return _mouseMoveImage;
            }
            set
            {
                _mouseMoveImage = value;
            }
        }

        public Image MouseDownImage
        {
            get
            {
                return _mouseDownImage;
            }
            set
            {
                _mouseDownImage = value;
            }
        }

        public Image NormalImage
        {
            get
            {
                return _normalImage;
            }
            set
            {
                _normalImage = value;
            }
        }

 

整个类的代码如下

 

C#仿QQ皮肤-ComboBox 控件实现_sed_03C#仿QQ皮肤-ComboBox 控件实现_ide_04代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using CRD.Common;

namespace CRD.WinUI.Misc
{
    public class ComboBox : System.Windows.Forms.ComboBox
    {
        //鼠标Move事件时图片
        private Image _mouseMoveImage = null;

        //鼠标mouseDown事件时图片
        private Image _mouseDownImage = null;
        //
        private Image _normalImage = null;


        public ComboBox()
            : base()
        {
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            //设置为手动绘制
            this.DrawMode = DrawMode.OwnerDrawFixed;
            //设置固定的DropDownList样式
            this.DropDownStyle = ComboBoxStyle.DropDownList;
            this.UpdateStyles();
        }

        protected override void OnCreateControl()
        {
            base.OnCreateControl();
            if (!DesignMode && this.Items.Count != 0)
            {
                this.DropDownHeight = this.Items.Count * 17;
            }
            ResetBitmap();
        }

        const int WM_ERASEBKGND = 0x14;
        const int WM_PAINT = 0xF;
        const int WM_NC_HITTEST = 0x84;
        const int WM_NC_PAINT = 0x85;
        const int WM_PRINTCLIENT = 0x318;
        const int WM_SETCURSOR = 0x20;

        protected override void WndProc(ref Message m)
        {
            IntPtr hDC = IntPtr.Zero;
            Graphics gdc = null;

            switch (m.Msg)
            {
                case 133:
                    hDC = Win32.GetWindowDC(m.HWnd);
                    gdc = Graphics.FromHdc(hDC);
                    Win32.SendMessage(this.Handle, WM_ERASEBKGND, hDC.ToInt32(), 0);
                    SendPrintClientMsg();
                    Win32.SendMessage(this.Handle, WM_PAINT, 0, 0);
                    OverrideControlBorder(gdc);
                    m.Result = (IntPtr)1;    // indicate msg has been processed
                    Win32.ReleaseDC(m.HWnd, hDC);
                    gdc.Dispose();
                    break;
                case WM_PAINT:
                    base.WndProc(ref m);
                    hDC = Win32.GetWindowDC(m.HWnd);
                    gdc = Graphics.FromHdc(hDC);

                    OverrideDropDown(gdc);
                    OverrideControlBorder(gdc);
                    Win32.ReleaseDC(m.HWnd, hDC);
                    gdc.Dispose();
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            } 
        }

        private static int DropDownButtonWidth = 17;

        private void OverrideDropDown(Graphics g)
        {
            if (DesignMode) return;

            Rectangle rect = new Rectangle(this.Width - DropDownButtonWidth, 0, DropDownButtonWidth, this.Height);

            g.FillRectangle(new SolidBrush(Color.White), rect);

            if (this.Enabled)
            {
                if (_mouseEnter)
                {
                    g.DrawImage(this.MouseMoveImage, new Rectangle(this.Width - 20, 3, 16, 16));
                }
                else
                {
                    g.DrawImage(this.NormalImage, new Rectangle(this.Width - 20, 3, 16, 16));
                }
            }
            else
            {
                g.DrawImage(Shared.NotEnableDrawButton, new Rectangle(this.Width - 20, 3, 16, 16));
            }
        }

        private Pen BorderPen = new Pen(Shared.ControlBorderBackColor, 2);
        private Pen BorderPenControl = new Pen(Shared.ControlBorderBackColor, 2);

        private void OverrideControlBorder(Graphics g)
        {
            g.DrawRectangle(new Pen(Shared.ControlBorderBackColor, 2), new Rectangle(0, 0, this.Width, this.Height));

        }

        private void SendPrintClientMsg()
        {
            // We send this message for the control to redraw the client area
            Graphics gClient = this.CreateGraphics();
            IntPtr ptrClientDC = gClient.GetHdc();
            Win32.SendMessage(this.Handle, WM_PRINTCLIENT, ptrClientDC.ToInt32(), 0);
            gClient.ReleaseHdc(ptrClientDC);
            gClient.Dispose();
        }


        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            Graphics g = e.Graphics;
            //绘制区域
            Rectangle r = e.Bounds;

            Font fn = null;
            if (e.Index >= 0)
            {
                if (e.State == DrawItemState.None)
                {
                    //设置字体、字符串格式、对齐方式
                    fn = e.Font;
                    string s = this.Items[e.Index].ToString ();
                    StringFormat sf = new StringFormat();
                    sf.Alignment = StringAlignment.Near;
                    //根据不同的状态用不同的颜色表示
                    if (e.State == (DrawItemState.NoAccelerator | DrawItemState.NoFocusRect))
                    {
                        e.Graphics.FillRectangle(new SolidBrush(Color.Red), r);
                        e.Graphics.DrawString(s, fn, new SolidBrush(Color.Black), r, sf);
                        e.DrawFocusRectangle();
                    }
                    else
                    {
                        e.Graphics.FillRectangle(new SolidBrush(Color.White), r);
                        e.Graphics.DrawString(s, fn, new SolidBrush(Shared.FontColor), r, sf);
                        e.DrawFocusRectangle();
                    }
                }
                else
                {
                    fn = e.Font;
                    StringFormat sf = new StringFormat();
                    sf.Alignment = StringAlignment.Near;
                    string s = this.Items[e.Index].ToString ();
                    e.Graphics.FillRectangle(new SolidBrush(Shared.ControlBackColor), r);
                    e.Graphics.DrawString(s, fn, new SolidBrush(Shared.FontColor), r, sf);
                    
                }
            }
        }

        private bool _mouseEnter = false;

        protected override void OnMouseEnter(EventArgs e)
        {
            _mouseEnter = true;

            IntPtr hDC = IntPtr.Zero;
            Graphics gdc = null;
            hDC = Win32.GetWindowDC(this.Handle);
            gdc = Graphics.FromHdc(hDC);

            gdc.DrawImage(Shared.MouseMoveDrawButton, new Rectangle(this.Width - 20, 3, 16, 16));

            Win32.ReleaseDC(this.Handle, hDC);
            gdc.Dispose();

            base.OnMouseEnter(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            _mouseEnter = false;
            IntPtr hDC = IntPtr.Zero;
            Graphics gdc = null;
            hDC = Win32.GetWindowDC(this.Handle);
            gdc = Graphics.FromHdc(hDC);

            gdc.DrawImage(Shared.NomalDrawButton, new Rectangle(this.Width - 20, 3, 16, 16));

            Win32.ReleaseDC(this.Handle, hDC);
            base.OnMouseLeave(e);
        }

        public Image MouseMoveImage
        {
            get
            {
                return _mouseMoveImage;
            }
            set
            {
                _mouseMoveImage = value;
            }
        }

        public Image MouseDownImage
        {
            get
            {
                return _mouseDownImage;
            }
            set
            {
                _mouseDownImage = value;
            }
        }

        public Image NormalImage
        {
            get
            {
                return _normalImage;
            }
            set
            {
                _normalImage = value;
            }
        }

        public void ResetBitmap()
        {
            this.NormalImage = Shared.NomalDrawButton;
            this.MouseDownImage = Shared.MouseDownDrawButton;
            this.MouseMoveImage = Shared.MouseMoveDrawButton;
        }


    }
}