下拉列表与可滚动面板中的组合框分开
Posted
技术标签:
【中文标题】下拉列表与可滚动面板中的组合框分开【英文标题】:DropDown List separated from the ComboBox in a scrollable Panel 【发布时间】:2021-09-01 14:43:21 【问题描述】:如果我将 ComboBox 放在 Panel 内并将 Panel 的 AutoScroll
属性设置为 True,则滚动面板时不会重绘 ComboBox 列表。这导致 DropDown 浮动在其初始位置并覆盖其他控件。可以通过以下方式轻松重现此问题:
-
创建一个新的 Windows 窗体项目
在表单中放置一个面板(使其足够大以容纳几个 ComboBox)
垂直堆叠放置一些组合框(添加了一些项目),以便它们放置在面板边界之外(这将使滚动条出现在设计器中)
运行项目并单击其中一个组合框以显示下拉菜单
将鼠标移动到面板上的某个位置并滚动鼠标滚轮
当您滚动时,您会注意到 ComboBox 会上下移动,但 ComboBox 下拉列表会保留在之前绘制的位置。
我认为这是 Windows 窗体中的一个错误,但我在任何地方都找不到它的文档。
是否有针对此问题的解决方法,因此我不会出现此行为?
【问题讨论】:
【参考方案1】:在 Windows 10 中,当您的窗体使用其主题时,可滚动 UI 元素的行为体现了这一功能:滚动操作被调度到鼠标指针下的任何控件,完全忽略许多控件的功能都依赖于鼠标捕获。
在这种情况下,不会通知持有列表控件句柄的 NativeWindow ComboBox.Location
已更改。
请注意,ScrollableControl Container 也不会引发 Scroll 事件。
您可以测试从标准 ComboBox 派生的自定义控件。
它根据其 CloseDropDownOnScroll
属性的值修改其 Location 属性更改时的默认行为:
CloseDropDownOnScroll = true:
当控件的位置发生变化并显示下拉列表时,它会隐藏列表控件。
CloseDropDownOnScroll = false:
在同样的情况下,如果 ComboBox 和 List Control 可以放入 Parent Container 的客户区,则 DropDown List 被移动,跟随其 ComboBox,否则隐藏。
控件使用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
【讨论】:
【参考方案2】:它也发生在Windows 8.1
。一种方法是检测Location
何时更改,然后重新显示下拉菜单。注意:LocationChanged
可能不会被触发,具体取决于正在滚动的父控件。
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form f5 = new Form();
ComboBox combo5 = new ComboBox();
combo5.Margin = new Padding(50, 100, 0, 0);
combo5.Items.AddRange(new Object[] "A", "B", "C" );
FlowLayoutPanel panel = new FlowLayoutPanel();
panel.Controls.Add(combo5);
f5.Controls.Add(panel);
combo5.LocationChanged += delegate
if (combo5.DroppedDown)
// this could be replaced with better code that grabs the handle of the list box window
// and then sets the location of the list box
combo5.DroppedDown = false;
combo5.DroppedDown = true;
;
panel.AutoScroll = true;
panel.AutoScrollMinSize = new Size(500, 500);
panel.Dock = DockStyle.Fill;
Application.Run(f5);
【讨论】:
以上是关于下拉列表与可滚动面板中的组合框分开的主要内容,如果未能解决你的问题,请参考以下文章