支援多選的ComboBox
阿新 • • 發佈:2019-02-05
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兩個控制元件組成,如果你需要更多的功能,完全可以在此基礎上新增,希望對你有幫助