简体   繁体   中英

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

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 change label text when I hover mouse over a combobox item? .

How can I do it the similar way in Windows Forms, or is there an alternate way?

Class ComboBoxListEx:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;

[DesignerCategory("Code")]
public class ComboBoxListEx : ComboBox
{
    private int listItem = -1;
    private const int CB_GETCURSEL = 0x0147;

    public event EventHandler<ListItemSelectionChangedEventArgs> ListItemSelectionChanged;

    protected virtual void OnListItemSelectionChanged(ListItemSelectionChangedEventArgs e)
        => this.ListItemSelectionChanged?.Invoke(this, e);

    public ComboBoxListEx() { }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg)
        {
            case CB_GETCURSEL:
                int selItem = m.Result.ToInt32();
                if (listItem != selItem)
                {
                    listItem = selItem;
                    OnListItemSelectionChanged(new ListItemSelectionChangedEventArgs(
                        listItem, listItem < 0 ? string.Empty : this.GetItemText(this.Items[listItem]))
                    );
                }
                break;
            default:
                // Add Case switches to handle other events
                break;
        }
    }

    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; }
    }
}         


private void comboBoxListEx1_ListItemSelectionChanged(object sender, ComboBoxListEx.ListItemSelectionChangedEventArgs e)
{
    label15.Text = e.ItemText;
}

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<ListItemSelectionChangedEventArgs> 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<COMBOBOXINFO>();
    }

    internal static IntPtr GetComboBoxListInternal(IntPtr cboHandle)
    {
        var cbInfo = new COMBOBOXINFO();
        cbInfo.Init();
        GetComboBoxInfo(cboHandle, ref cbInfo);
        return cbInfo.hwndList;
    }
}

Works like this:

ComboBox 自定义列表悬停选择事件

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM