1. 程式人生 > >支援多選的ComboBox

支援多選的ComboBox

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兩個控制元件組成,如果你需要更多的功能,完全可以在此基礎上新增,希望對你有幫助