Raise an event when I hover the mouse over a ComboBox item

前端 未结 1 701
醉梦人生
醉梦人生 2021-01-25 03:08

I\'m not able to find an event to fire when I hover my ComboBox Items.
I\'m using windows form to build an application.
I found a something similar for WPF:
how to

1条回答
  •  清酒与你
    2021-01-25 03:31

    You can create a Custom Control, derived from ComboBox, override its WndProc method to intercept the CB_GETCURSEL message.

    Call base.WndProc(ref m) first. When the message is processed, the Message object's m.Result property is set to a value (as IntPtr) that represents the Item currently tracked in the ListBox (the Item highlighted when the Mouse Pointer hovers it).

    Note: prior to .Net Framework 4.8, the CB_GETCURSEL message result is not bubbled up automatically: LB_GETCUSEL must be sent to the child ListBox to get the index of the Item currently highlighted.
    The ListBox handle is retrieved using GetComboBoxInfo: it could be also accessed using reflection (the private ChildListAutomationObject property returns the ListBox AutomationElement, which provides the handle), or sending a CB_GETCOMBOBOXINFO message (but it's the same as calling GetComboBoxInfo()).


    This custom ComboBox raises an Event, ListItemSelectionChanged, with a custom EventArgs object, ListItemSelectionChangedEventArgs, which exposes two public properties: ItemIndex and ItemText, set to the Index and Text of the hovered item.


    using System.ComponentModel;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    [DesignerCategory("Code")]
    public class ComboBoxListEx : ComboBox
    {
        private const int CB_GETCURSEL = 0x0147;
        private int listItem = -1;
        IntPtr listBoxHandle = IntPtr.Zero;
    
        public event EventHandler ListItemSelectionChanged;
    
        protected virtual void OnListItemSelectionChanged(ListItemSelectionChangedEventArgs e)
            => this.ListItemSelectionChanged?.Invoke(this, e);
    
        public ComboBoxListEx() { }
    
        // .Net Framework prior to 4.8 - get the handle of the ListBox
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            listBoxHandle = GetComboBoxListInternal(this.Handle);
        }
    
        protected override void WndProc(ref Message m)
        {
            int selItem = -1;
            base.WndProc(ref m);
    
            switch (m.Msg) {
                case CB_GETCURSEL:
                    selItem = m.Result.ToInt32();
                    break;
                // .Net Framework before 4.8
                // case CB_GETCURSEL can be left there or removed: it's always -1
                case 0x0134: 
                    selItem = SendMessage(listBoxHandle, LB_GETCUSEL, 0, 0);
                    break;
                default:
                    // Add Case switches to handle other events
                    break;
            }
            if (listItem != selItem) {
                listItem = selItem;
                OnListItemSelectionChanged(new ListItemSelectionChangedEventArgs(
                    listItem, listItem < 0 ? string.Empty : this.GetItemText(this.Items[listItem]))
                );
            }
        }
    
        public class ListItemSelectionChangedEventArgs : EventArgs
        {
            public ListItemSelectionChangedEventArgs(int idx, string text) {
                this.ItemIndex = idx;
                this.ItemText = text;
            }
            public int ItemIndex { get; private set; }
            public string ItemText { get; private set; }
        }
    
        // -------------------------------------------------------------
        // .Net Framework prior to 4.8
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int lParam);
    
        private const int LB_GETCUSEL = 0x0188;
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct COMBOBOXINFO
        {
            public int cbSize;
            public Rectangle rcItem;
            public Rectangle rcButton;
            public int buttonState;
            public IntPtr hwndCombo;
            public IntPtr hwndEdit;
            public IntPtr hwndList;
            public void Init() => this.cbSize = Marshal.SizeOf();
        }
    
        internal static IntPtr GetComboBoxListInternal(IntPtr cboHandle)
        {
            var cbInfo = new COMBOBOXINFO();
            cbInfo.Init();
            GetComboBoxInfo(cboHandle, ref cbInfo);
            return cbInfo.hwndList;
        }
    }
    

    Works like this:

    0 讨论(0)
提交回复
热议问题