Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
299 views
in Technique[技术] by (71.8m points)

c# - 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;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 prior to 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 : GetItemText(Items[listItem]))
            );
        }
    }

    public class ListItemSelectionChangedEventArgs : EventArgs
    {
        public ListItemSelectionChangedEventArgs(int idx, string text) {
            ItemIndex = idx;
            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 Custom List Hover Selection Events


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...