支持多选的ComboBox

来源:互联网 发布:功能测试软件 编辑:程序博客网 时间:2024/04/30 00:46

 最近需要用到一个支持多选的ComboBox控件,但.Net控件族里没有类似ComboBox且支持多选的控件,所以写了一个自定义控件(参考了博客园“大气象”写的控件&nbsp; <a href="http://www.cnblogs.com/greatverve/archive/2011/07/19/ComboBox-Multi-Pic.html">http://www.cnblogs.com/greatverve/archive/2011/07/19/ComboBox-Multi-Pic.html</a>及),在原作者控件的基础上完善了一些功能,比如鼠标点击下拉列表外部时隐藏下拉列表,记忆选项及鼠标经过下拉列表时highlight选项等。代码如下:

using System;using System.ComponentModel;using System.Diagnostics;using System.Drawing;using System.Runtime.InteropServices;using System.Windows.Forms;using System.Reflection;namespace CustomComboBox{    public class MultiSelectableComboBox : System.Windows.Forms.ComboBox    {        #region Private fields        private int clickedCounts = -1;        private ListBox selfLst = new ListBox();        MouseAndKeyHook mouseHooker = new MouseAndKeyHook();        private static readonly char Separator = ',';        private static readonly int DropDownButtonWidth = 21;        private static readonly string Empty = "";        #endregion        public MultiSelectableComboBox()        {            //Set this property to support redraw the control            this.DropDownHeight = 1;            this.DrawMode = DrawMode.OwnerDrawFixed;            selfLst.SelectionMode = SelectionMode.MultiExtended;            selfLst.KeyUp += new KeyEventHandler(lst_KeyUp);            selfLst.MouseUp += new MouseEventHandler(lst_MouseUp);            selfLst.KeyDown += new KeyEventHandler(lst_KeyDown);            selfLst.MouseMove += new MouseEventHandler(lst_MouseMove);            selfLst.VisibleChanged += new EventHandler(selfLst_VisibleChanged);            mouseHooker.OnMouseActivity += new MouseEventHandler(mouseHooker_OnMouseActivity);        }        private void mouseHooker_OnMouseActivity(object sender, MouseEventArgs e)        {            Point mousePoint = selfLst.PointToClient(new Point(e.X, e.Y));            bool withinListBoxWidth = (mousePoint.X >= 0 && mousePoint.X < selfLst.Width);            bool withinListBoxHight = (mousePoint.Y >= 0 && mousePoint.Y < selfLst.Height);            bool withinListBox = withinListBoxWidth && withinListBoxHight;            int dropDownButtonleft = this.Width - DropDownButtonWidth;            mousePoint = this.PointToClient(new Point(e.X, e.Y));            bool withinDropDownButtonWidth = (mousePoint.X > dropDownButtonleft && mousePoint.X < this.Width);            bool withinDropDownButtonHight = (mousePoint.Y >= 0 && mousePoint.Y < this.Height);            bool withinDropDownButton = withinDropDownButtonWidth && withinDropDownButtonHight;            if (e.Clicks > 0 && !withinListBox && !withinDropDownButton)            {                selfLst.Hide();                clickedCounts = 1;                mouseHooker.Stop();            }        }        #region Properties        [Description("Get those selected items"), Category("Data")]        public ListBox.SelectedObjectCollection SelectedItems        {            get            {                return selfLst.SelectedItems;            }        }        #endregion        #region Override methods        protected override void OnKeyUp(KeyEventArgs e)        {            base.OnKeyUp(e);            bool Pressed = (e.Control && ((e.KeyData & Keys.A) == Keys.A));            if (Pressed)            {                for (int i = 0; i < selfLst.Items.Count; i++)                {                    selfLst.SetSelected(i, true);                }            }        }        protected override void OnMouseDown(MouseEventArgs e)        {            base.OnMouseDown(e);            this.DroppedDown = false;            selfLst.Focus();        }        protected override void OnDropDown(EventArgs e)        {            base.OnDropDown(e);            clickedCounts++;            if (selfLst.Parent == null)            {                selfLst.Parent = this.Parent;            }            if (clickedCounts % 2 == 0)            {                clickedCounts = 0;                selfLst.Items.Clear();                // Set the style and location info for listBox.                selfLst.ItemHeight = this.ItemHeight;                selfLst.BorderStyle = BorderStyle.FixedSingle;                selfLst.Size = new Size(this.DropDownWidth, this.ItemHeight * (this.MaxDropDownItems - 1) - (int)this.ItemHeight / 2);                selfLst.Location = new Point(this.Left, this.Top + this.ItemHeight + 6);                // Bring this lsitBox to front                this.Parent.Controls.SetChildIndex(selfLst, 0);                selfLst.BeginUpdate();                if (this.Items.Count != 0)                {                    for (int i = 0; i < this.Items.Count; i++)                    {                        selfLst.Items.Add(this.Items[i]);                    }                }                selfLst.EndUpdate();                selfLst.Show();                if (!this.Parent.Controls.Contains(selfLst))                {                    this.Parent.Controls.Add(selfLst);                }                mouseHooker.Start();            }            else            {                selfLst.Hide();                clickedCounts = 1;                mouseHooker.Stop();            }        }        protected override void OnDataSourceChanged(EventArgs e)        {            base.OnDataSourceChanged(e);            selfLst.BeginUpdate();            // If the comboBox's dataSource is not null then clear all items of ListBox, and set            // the listBox's dataSource with the comboBox's dataSource.            if (this.DataSource != null)            {                selfLst.Items.Clear();                selfLst.DataSource = this.DataSource;                selfLst.DisplayMember = this.DisplayMember;                selfLst.ValueMember = this.ValueMember;            }            selfLst.EndUpdate();            selfLst.Refresh();        }        protected override void OnTextUpdate(EventArgs e)        {            base.OnTextUpdate(e);            clickedCounts = -1;            this.OnDropDown(e);        }        protected override void OnSelectedIndexChanged(EventArgs e)        {            base.OnSelectedIndexChanged(e);            selfLst.SelectedIndex = this.SelectedIndex;        }        #endregion        #region Self listBox's events        private void lst_KeyUp(object sender, KeyEventArgs e)        {            this.OnKeyUp(e);        }        private void lst_MouseUp(object sender, MouseEventArgs e)        {            try            {                this.Text = Empty;                for (int i = 0; i < selfLst.SelectedItems.Count; i++)                {                    if (i == 0)                        this.Text = selfLst.SelectedItems[i].ToString();                    else                    {                        this.Text = this.Text + Separator.ToString() + selfLst.SelectedItems[i].ToString();                    }                }            }            catch            {                this.Text = Empty;            }            bool isControlPressed = (Control.ModifierKeys == Keys.Control);            bool isShiftPressed = (Control.ModifierKeys == Keys.Shift);            if (isControlPressed || isShiftPressed)            {                selfLst.Show();                clickedCounts = 0;            }            else            {                selfLst.Hide();                clickedCounts = 1;            }        }        private void lst_KeyDown(object sender, KeyEventArgs e)        {            switch (e.KeyData)            {                case Keys.Down:                    if (selfLst.SelectedItems.Count != 0)                    {                        this.Text = selfLst.SelectedItem.ToString();                    }                    else                        this.Text = this.Items[0].ToString();                    break;                case Keys.Up:                    if (selfLst.SelectedItems.Count != 0)                    {                        this.Text = selfLst.SelectedItem.ToString();                    }                    else                        this.Text = this.Items[0].ToString();                    break;            }        }        private void lst_MouseMove(object sender, MouseEventArgs e)        {            int hoverIndex = -1;            bool hasMultiSelectedItem = (selfLst.SelectedItems.Count > 1);            bool isControlPressed = (Control.ModifierKeys == Keys.Control);            bool isShiftPressed = (Control.ModifierKeys == Keys.Shift);            hoverIndex = selfLst.IndexFromPoint(e.X, e.Y);            if (hoverIndex != -1)            {                // If the Ctrl or Shift key dosen't pressed or the listBox dosen't have multi selected items                if (!(isControlPressed || isShiftPressed || hasMultiSelectedItem))                {                    selfLst.ClearSelected();                    selfLst.SetSelected(hoverIndex, true);                }            }        }        private void selfLst_VisibleChanged(object sender, EventArgs e)        {            if (selfLst.Visible && !string.IsNullOrEmpty(this.Text))            {                string[] selectedItems = { };                selectedItems = this.Text.Split(Separator);                selfLst.ClearSelected();                for (int i = 0; i < selectedItems.Length; i++)                {                    if (selfLst.Items.Contains(selectedItems[i]))                    {                        int targetItemIndex = selfLst.Items.IndexOf(selectedItems[i]);                        if (targetItemIndex != -1)                        {                            selfLst.SetSelected(targetItemIndex, true);                        }                    }                }            }        }        #endregion    }    #region Mouse and Key Hook    public class MouseAndKeyHook : object    {        public event MouseEventHandler OnMouseActivity;        public event KeyEventHandler KeyDown;        public event KeyPressEventHandler KeyPress;        public event KeyEventHandler KeyUp;        public delegate IntPtr HookProc(int nCode, Int32 wParam, IntPtr lParam);        static IntPtr hMouseHook = IntPtr.Zero;        static IntPtr hKeyboardHook = IntPtr.Zero;        public const int WH_MOUSE = 7;        public const int WH_MOUSE_LL = 14;        public const int WH_KEYBOARD_LL = 13;        HookProc MouseHookProcedure;        HookProc KeyboardHookProcedure;        [StructLayout(LayoutKind.Sequential)]        public class POINT        {            public int x;            public int y;        }        [StructLayout(LayoutKind.Sequential)]        public class MouseHookStruct        {            public POINT pt;            public uint mouseData;            public uint flags;            public uint time;            public IntPtr dwExtraInfo;        }        [StructLayout(LayoutKind.Sequential)]        public class KeyboardHookStruct        {            public int vkCode;            public int scanCode;            public int flags;            public IntPtr dwExtraInfo;        }        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        public static extern bool UnhookWindowsHookEx(IntPtr idHook);        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, Int32 wParam, IntPtr lParam);        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]        private static extern IntPtr GetModuleHandle(string lpModuleName);        public void Start()        {            if (hMouseHook == IntPtr.Zero)            {                MouseHookProcedure = new HookProc(MouseHookProc);                hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookProcedure,                                IntPtr.Zero, AppDomain.GetCurrentThreadId());                if (hMouseHook == IntPtr.Zero)                {                    Stop();                }            }            //if (hKeyboardHook == IntPtr.Zero)            //{            //    KeyboardHookProcedure = new HookProc(KeyboardHookProc);            //    hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,            //        KeyboardHookProcedure,            //        Marshal.GetHINSTANCE(            //        Assembly.GetExecutingAssembly().GetModules()[0]),            //        0);            //    if (hKeyboardHook == IntPtr.Zero)            //    {            //        Stop();            //        throw new Exception("SetWindowsHookEx ist failed.");            //    }            //}        }        public void Stop()        {            bool retMouse = true;            bool retKeyboard = true;            if (hMouseHook != IntPtr.Zero)            {                retMouse = UnhookWindowsHookEx(hMouseHook);                hMouseHook = IntPtr.Zero;            }            if (hKeyboardHook != IntPtr.Zero)            {                retKeyboard = UnhookWindowsHookEx(hKeyboardHook);                hKeyboardHook = IntPtr.Zero;            }            if (!(retMouse && retKeyboard)) throw new Exception("UnhookWindowsHookEx failed.");        }        private const int WM_MOUSEMOVE = 0x200;        private const int WM_LBUTTONDOWN = 0x201;        private const int WM_RBUTTONDOWN = 0x204;        private const int WM_MBUTTONDOWN = 0x207;        private const int WM_LBUTTONUP = 0x202;        private const int WM_RBUTTONUP = 0x205;        private const int WM_MBUTTONUP = 0x208;        private const int WM_LBUTTONDBLCLK = 0x203;        private const int WM_RBUTTONDBLCLK = 0x206;        private const int WM_MBUTTONDBLCLK = 0x209;        private IntPtr MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)        {            if ((nCode >= 0) && (OnMouseActivity != null))            {                MouseButtons button = MouseButtons.None;                switch (wParam)                {                    case WM_LBUTTONDOWN:                        button = MouseButtons.Left;                        break;                    case WM_RBUTTONDOWN:                        button = MouseButtons.Right;                        break;                    case WM_MBUTTONDOWN:                        button = MouseButtons.Middle;                        break;                }                int clickCount = 0;                if (button != MouseButtons.None)                {                    if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK)                    {                        clickCount = 2;                    }                    else                    {                        clickCount = 1;                    }                }                MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));                MouseEventArgs e = new MouseEventArgs(                    button,                    clickCount,                    MyMouseHookStruct.pt.x,                    MyMouseHookStruct.pt.y,                    0);                OnMouseActivity(this, e);            }            return CallNextHookEx(hMouseHook, nCode, wParam, lParam);        }        [DllImport("user32")]        public static extern int ToAscii(int uVirtKey,            int uScanCode,            byte[] lpbKeyState,            byte[] lpwTransKey,            int fuState);        [DllImport("user32")]        public static extern int GetKeyboardState(byte[] pbKeyState);        private const int WM_KEYDOWN = 0x100;        private const int WM_KEYUP = 0x101;        private const int WM_SYSKEYDOWN = 0x104;        private const int WM_SYSKEYUP = 0x105;        private IntPtr KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)        {            if ((nCode >= 0) && (KeyDown != null || KeyUp != null || KeyPress != null))            {                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));                if (KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))                {                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;                    KeyEventArgs e = new KeyEventArgs(keyData);                    KeyDown(this, e);                }                if (KeyPress != null && wParam == WM_KEYDOWN)                {                    byte[] keyState = new byte[256];                    GetKeyboardState(keyState);                    byte[] inBuffer = new byte[2];                    if (ToAscii(MyKeyboardHookStruct.vkCode,                        MyKeyboardHookStruct.scanCode,                        keyState,                        inBuffer,                        MyKeyboardHookStruct.flags) == 1)                    {                        KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);                        KeyPress(this, e);                    }                }                if (KeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))                {                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;                    KeyEventArgs e = new KeyEventArgs(keyData);                    KeyUp(this, e);                }            }            return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);        }        /// <summary>Gets a reference to the Process instance for the running ehshell.exe</summary>        private Process GetEhShellProcess()        {            // Get the current terminal services session ID            int currentSessionId;            using (Process currentProcess = Process.GetCurrentProcess()) currentSessionId = currentProcess.SessionId;            // Get all ehome processes on the machine, and find the one in the current session            Process[] procs = Process.GetProcessesByName("ehshell");            Process ehshell = null;            for (int i = 0; i < procs.Length; i++)            {                if (ehshell == null && procs[i].SessionId == currentSessionId) ehshell = procs[i];                else procs[i].Dispose();            }            return ehshell;        }    }    #endregion}

 

       注意在该自定义控件中包含两个类,“MultiSelectableComboBox ”,“MouseAndKeyHook”, 后者通过使用Hook来实现鼠标和键盘操作的监控,该控件由ComboBox和ListBox两个控件组成,如果你需要更多的功能,完全可以在此基础上添加,希望对你有帮助