问题
In a recent project of mine I had a ComboBox with items that exceeded the ComboBox drop-down list length.
I wanted to show the items full text in a tool tip.
Unfortunately, the .NET framework doesn't provide a built-in solution for this.
回答1:
Here's my own solution
You can find the full code, a more detailed explanation and an example project on my github. https://github.com/MaxBGitHub/MouseOverItemComboBox
Prerequisites
The provided code is for the Windows Forms library of the C# .NET 4.5.2 framework and was written with Visual Studio 2017.
In order to get this to work you need to utilize some native WinAPI functions and structs. The following native functions and native structs will be used:
- RECT
- POINT
- COMBOBOXINFO
- LBItemFromPt
- GetComboBoxInfo
Just copy paste the structs from pinvoke. No need to wrap your head around these. The LBItemFromPt function and the GetComboBoxInfo function are pretty straight forward. If you do not know how to implement these, just go to the github page above or read the code below.
Custom ComboBox class implementation
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;
}
}
}
}
来源:https://stackoverflow.com/questions/61748198/how-to-get-the-index-of-a-combobox-item-under-the-cursor