在 Windows 10 中,当您的窗体使用其主题时,可滚动 UI 元素的行为体现了这一功能:滚动操作被调度到鼠标指针下的任何控件,完全忽略许多控件的功能都依赖于鼠标捕获。
在这种情况下,不会通知持有列表控件句柄的 NativeWindow ComboBox.Location 已更改。
请注意,ScrollableControl Container 也不会引发 Scroll 事件。
您可以测试从标准 ComboBox 派生的自定义控件。
它根据其 CloseDropDownOnScroll 属性的值修改其 Location 属性更改时的默认行为:
控件使用GetComboBoxInfo() 函数来检索下拉列表控件的句柄和GetWindowRect() 来获取它的大小。
然后在必要时使用 SetWindowPos() 函数移动 ListControl NativeWindow。
在需要时调整或扩展其功能应该很简单。
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[DesignerCategory("code")]
public class ComboBoxExt : ComboBox
{
IntPtr listHandle = IntPtr.Zero;
private Size listSize = Size.Empty;
public ComboBoxExt() { }
public bool CloseDropDownOnScroll { get; set; } = true;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
listHandle = GetComboBoxListInternal(this.Handle);
if (GetWindowRect(listHandle, out Rectangle rect)) {
listSize = rect.Size;
}
}
protected override void OnLocationChanged(EventArgs e)
{
base.OnLocationChanged(e);
if (!DesignMode && DroppedDown) {
if (CloseDropDownOnScroll) {
DroppedDown = false;
}
else {
var rect = RectangleToScreen(ClientRectangle);
if (Bottom > 0 && (Bottom + listSize.Height) < this.Parent.ClientSize.Height) {
SetWindowPos(listHandle, IntPtr.Zero, rect.Left, rect.Bottom, 0, 0, swpflags);
}
else {
DroppedDown = false;
}
}
}
}
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_ASYNCWINDOWPOS = 0x4000;
uint swpflags = SWP_NOSIZE | SWP_NOZORDER | SWP_ASYNCWINDOWPOS;
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetWindowRect(IntPtr hwnd, out Rectangle lpRect);
[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;
}
}
▶ 不要从this.Handle 和this.Parent 中删除this