Вы можете создать пользовательский элемент управления, производный от ComboBox, переопределить его WndPro c метод для перехвата сообщения CB_GETCURSEL .
Сначала позвоните base.WndProc(ref m)
. Когда сообщение обрабатывается, свойству m.Result
объекта Message присваивается значение (например, IntPtr
), которое представляет элемент, отслеживаемый в данный момент в ListBox (элемент, выделенный при наведении на него указателя мыши) ,
► Примечание: до. Net Framework 4.8, результат сообщения CB_GETCURSEL
не всплывает автоматически: LB_GETCUSEL
должен быть отправлен на дочерний ListBox для получения индекса элемента, который в данный момент выделен .
Дескриптор ListBox извлекается с помощью GetComboBoxInfo : к нему также можно получить доступ с помощью отражения (свойство private ChildListAutomationObject
возвращает элемент ListBox AutomationElement, который обеспечивает дескриптор), или отправка сообщения CB_GETCOMBOBOXINFO
(но это то же самое, что вызов GetComboBoxInfo()
).
Этот пользовательский ComboBox генерирует событие, ListItemSelectionChanged
, с пользовательским объектом EventArgs
, ListItemSelectionChangedEventArgs
, который предоставляет две публикации c свойства: ItemIndex
и 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 before 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 : this.GetItemText(this.Items[listItem]))
);
}
}
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; }
}
// -------------------------------------------------------------
// .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;
}
}
Работает так:
![ComboBox Custom List Hover Selection Events](https://i.stack.imgur.com/goVjz.png)