【问题标题】:Raise an event when I hover the mouse over a ComboBox item当我将鼠标悬停在 ComboBox 项目上时引发事件
【发布时间】:2020-04-11 06:17:44
【问题描述】:

当我将鼠标悬停在 ComboBox 项时,我找不到要触发的事件。
我正在使用 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;
}

【问题讨论】:

    标签: c# winforms winapi combobox custom-controls


    【解决方案1】:

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

    请先致电base.WndProc(ref m)。处理消息时,Message 对象的m.Result 属性设置为一个值(如IntPtr),表示当前在列表框中跟踪的项目(当鼠标指针悬停时突出显示的项目)。

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


    这个自定义 ComboBox 引发一个事件 ListItemSelectionChanged,并带有一个自定义 EventArgs 对象 ListItemSelectionChangedEventArgs,它公开了两个公共属性:@ 987654339@ItemText,设置为悬停项的索引和文本。


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

    像这样工作:

    【讨论】:

    • 完美。你能告诉我如何在 Designer.cs 中编写这个自定义事件吗?非常感谢您详细解释这一点。
    • 我在注释中写过:在“属性”面板中,单击闪电图标 ⚡:您会在其中找到 ListItemSelectionChanged 事件。像往常一样双击它,一个标准的处理程序将被添加到表单的代码中。
    • 这可能很愚蠢.. 我能够让一切正常工作,但我只在单击而不是在 MouseHover 时触发事件。事件代码为 private void comboBox31_ListItemSelectionChanged(object sender, ComboBoxListEx.ListItemSelectionChangedEventArgs e) { label15.Text = e.ItemText;设计者是 this.comboBox31.ListItemSelectionChanged += new System.EventHandler(this.comboBox31_ListItemSelectionChanged);
    • 我刚刚复制了您粘贴的内容(但与我在此处发布的内容相同),它按预期工作。尝试:1)构建一个新项目,只有一个表单,2)创建一个名为 ComboBoxListEx 的新类并粘贴此代码,3)构建解决方案,4)在工具箱中找到新控件,5)将它放在表单上,​​6) 添加一些项目,7) 在组合框旁边添加一个标签(所以,现在你有 comboBoxListEx1label1),8) 在属性面板中找到 ListItemSelectionChanged 事件,然后双击它,创建处理程序,9)在处理程序中,添加label1.Text = e.ItemText,10)运行项目。
    • 好吧,我添加了一个补丁,它应该可以在任何地方工作。你可以用这个代替上一课。很抱歉给您带来不便。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多