首页 > 解决方案 > 如何获取光标下ComboBox Item的索引?

问题描述

在我最近的一个项目中,我有一个 ComboBox,其中包含超过 ComboBox 下拉列表长度的项目。
我想在工具提示中显示项目全文。
不幸的是,.NET 框架没有为此提供内置解决方案。

标签: c#winformsdrop-down-menucomboboxmouseover

解决方案


这是我自己的解决方案

你可以在我的 github 上找到完整的代码、更详细的解释和示例项目。 https://github.com/MaxBGitHub/MouseOverItemComboBox

先决条件

提供的代码适用于 C# .NET 4.5.2 框架的 Windows 窗体库,并使用 Visual Studio 2017 编写。

为了让它工作,您需要利用一些本机 WinAPI 函数和结构。将使用以下本机函数和本机结构:

只需从 pinvoke 复制粘贴结构即可。没有必要把你的头绕在这些上面。LBItemFromPt 函数和 GetComboBoxInfo 函数非常简单。如果您不知道如何实现这些,只需转到上面的 github 页面或阅读下面的代码即可。

自定义 ComboBox 类实现

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


class MouseOverItemComboBox : ComboBox
{
    // Event delegate
    public delegate void OnMouseOverItem(object sender, MouseOverEventArgs e);
    // Event which the parent can subscribe to.
    public event OnMouseOverItem MouseOverItem;

    // The possible combo box button states.
    enum ButtonState {
        STATE_SYSTEM_NONE       = 0,            // Button exists and is not pressed.
        STATE_SYSTEM_INVISIBLE  = 0x00008000,   // There is no button.
        STATE_SYSTEM_PRESSED    = 0x00000008,   // Button is pressed.
    }

    /* Native COMBOBOXINFO struct implementation.
    ** Contains combo box status information. */
    [StructLayout(LayoutKind.Sequential)]
    struct COMBOBOXINFO
    {
        public int          cbSize;         // Size in bytes of struct.
        public RECT         rcItem;         // RECT that specifies the coordinates of the edit box.
        public RECT         rcButton;       // RECT that specifies the coordinates of the drop-down button.
        public ButtonState  stateButton;    // Drop-down button state.
        public IntPtr       hwndCombo;      // Handle to the combo box.
        public IntPtr       hwndEdit;       // Handle to the edit box.
        public IntPtr       hwndList;       // Handle to the drop-down list.
    }


    /* Sent to parent window of a list box before the system draws the list box.
    ** Can set text and background color of the list box by using the specified
    ** device context handle. */
    const int WM_CTLCOLORLISTBOX = 0x0134;


    // Native function that retreives information about the specified combo box.
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcomboboxinfo
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool GetComboBoxInfo(IntPtr hWnd, [In][Out] ref COMBOBOXINFO pcbi);

    // Native function that retreives the index of the item at the specified point in a list box.
    // https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-lbitemfrompt
    [DllImport("Comctl32.dll", SetLastError = true)]
    static extern int LBItemFromPt(IntPtr hLB, POINT pt, bool bAutoScroll);


    // Helper method which will invoke the MouseOverItem event.
    private void HandleMouseOverItem()
    {
        // cbSize must be set before calling GetComboBoxInfo.
        COMBOBOXINFO pcbi = new COMBOBOXINFO();         
        pcbi.cbSize = Marshal.SizeOf(pcbi);

        // Get combo box information.
        GetComboBoxInfo(Handle, ref pcbi);
        // Check for invalid pointer... just in case.
        if (pcbi.hwndList == IntPtr.Zero)
            return;

        // Current position of cursor.
        POINT pt = Cursor.Position;
        // LBItemFromPt will return the Index of the Item on success.
        int retVal = LBItemFromPt(pcbi.hwndList, pt, false);
        if (retVal == -1)
            return;

        // Invoke the event.
        MouseOverItem?.Invoke(this, new MouseOverEventArgs(retVal));
    }


    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            /* This message is sent by the list box of the combo box.
            ** It is sent before the system draws the list box.
            ** Whenever the cursor enters a list item this message will be
            ** sent to the parent i.e. the combox box.
            ** NOTE that this message is always sent twice.
            ** First time for drawing the default item background.
            ** Second time for drawing the highlighted item background. */
            case WM_CTLCOLORLISTBOX:
                {
                    // Let the helper method do the rest.
                    HandleMouseOverItem();
                    base.WndProc(ref m);
                    break;
                }
            default:
                {
                    base.WndProc(ref m);
                    break;
                }
        }
    }
}

推荐阅读