c# - 当我将鼠标悬停在组合框项目上时引发事件

标签 c# winforms winapi combobox custom-controls

当我将鼠标悬停在组合框项目上时,我无法找到要触发的事件。
我正在使用 Windows 窗体来构建应用程序。
我发现了 WPF 的类似内容:
how to change label text when I hover mouse over a combobox item? .

如何在 Windows 窗体中以类似的方式执行此操作,或者是否有其他方法?

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

最佳答案

您可以创建一个派生自 ComboBox 的自定义控件,覆盖其 WndProc拦截 CB_GETCURSEL 的方法消息。

首先调用base.WndProc(ref m)。处理消息时,Message对象的 m.Result 属性设置为一个值(如 IntPtr),该值表示 ListBox 中当前跟踪的 Item(当鼠标指针悬停在该 Item 上时突出显示该 Item)。

注意:.NET Framework 4.8 之前,CB_GETCURSEL 消息结果不会自动冒泡 :我们必须将LB_GETCURSEL发送到子ListBox以获取当前突出显示的项目的索引。
使用 GetComboBoxInfo 检索 ListBox 句柄:也可以使用反射来访问它(私有(private) ChildListAutomationObject 属性返回提供句柄的 ListBox AutomationElement),或发送 CB_GETCOMBOBOXINFO消息(但与调用 GetComboBoxInfo() 相同)。


此自定义 ComboBox 使用自定义 EventArgs 对象 ListItemSelectionChangedEventArgs 引发事件 ListItemSelectionChanged ,公开两个公共(public)属性:ItemIndexItemText,设置为 Index 和 Text悬停的项目。


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)
        => 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. Otherwise remove
            // case CB_GETCURSEL can be left there or removed: it's always -1
            case 0x0134: 
                selItem = SendMessage(listBoxHandle, LB_GETCURSEL, 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 only. Otherwise remove 
    [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_GETCURSEL = 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() => cbSize = Marshal.SizeOf<COMBOBOXINFO>();
    }

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

工作原理如下:

ComboBox Custom List Hover Selection Events

关于c# - 当我将鼠标悬停在组合框项目上时引发事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61152847/

相关文章:

c# - 使用自定义绘制的控件滚动面板

c - 如何为 CTRL+ 创建加速器?在代码中

c++ - 0xccccc 处的访问读取冲突

c# - 单击 asp 按钮获取下拉列表的值

c# - 在 telerik RadGridView comboboxcolumn 中更改下拉窗口

winforms - 更新数据 GridView 中按钮的文本

windows - 在 Windows 内核模式和用户模式之间切换的成本(以周期为单位)是多少?

c# - 带有dll的免费SFTP客户端,用于在C#中上传和下载文件

c# - 网站引用了一个使用 NHibernate 与数据库对话的控制台应用程序

c# - WCF 双工服务使用 net tcp : "Stream Security is required..."