.NET UserControl
(源自ScrollableControl
)必须能够显示水平和垂直滚动条。
调用者可以设置这些水平和垂直滚动条的可见性和范围:
UserControl.AutoScroll = true;
UserControl.AutoScrollMinSize = new Size(1000, 4000); //1000x4000 scroll area
注意: (
UserControl
ieScrollableControl
) 使用 Windows 标准指定机制WS_HSCROLL
和WS_VSCROLL
窗口样式来显示滚动条。也就是说:它们不会创建单独的 Windows 或 .NET 滚动控件,将它们定位在窗口的右侧/底部。Windows 具有显示一个或两个滚动条的标准机制。
如果用户滚动控件,UserControl
则会发送一个WM_HSCROLL
或WM_VSCROLL
消息。为了响应这些消息,我希望 ScrollableControl 使客户区无效,这在本机 Win32 中会发生:
switch (uMsg)
{
case WM_VSCROLL:
...
GetScrollInfo(...);
...
SetScrollInfo(...);
...
InvalidateRect(g_hWnd,
null, //erase entire client area
true, //background needs erasing too (trigger WM_ERASEBKGND));
break;
}
我需要使整个客户区失效。问题是UserControl(即ScrollableControl
)调用ScrollWindow
API 函数:
protected void SetDisplayRectLocation(int x, int y)
{
...
if ((nXAmount != 0) || ((nYAmount != 0) && base.IsHandleCreated))
{
...
SafeNativeMethods.ScrollWindowEx(new HandleRef(this, base.Handle), nXAmount, nYAmount, null, ref rectClip, NativeMethods.NullHandleRef, ref prcUpdate, 7);
}
...
}
ScrollableControl 不会在整个客户矩形上触发 InvalidateRect,而是尝试“挽救”客户区域中的现有内容。比如用户向上滚动,当前客户端内容被 下推,然后ScrollWindowEx
只有新发现的区域失效,触发一个WM_PAINT
:
在上图中,棋盘区域是无效的内容,必须在下一次 WM_PAINT 期间绘制。
就我而言,这不好。我的控件顶部包含一个“标题”(例如 listview 列标题)。进一步向下滚动此内容是不正确的:
它会导致视觉损坏。
我希望 ScrollableControl不使用ScrollWindowEx
,而只是使整个客户区无效。
我尝试覆盖OnScroll
受保护的方法:
protected override void OnScroll(ScrollEventArgs se)
{
base.OnScroll(se);
this.Invalidate();
}
但它会导致双重平局。
注意:我可以使用双缓冲来掩盖问题,但这不是真正的解决方案
- 不应在远程桌面/终端会话下使用双缓冲
- 很浪费CPU资源
- 这不是我要问的问题
我考虑使用 aControl
而不是UserControl
(即ScrollableControl
在继承链中之前)并手动添加 HScroll 或 VScroll .NET 控件 - 但这也是不可取的:
- Windows 已经提供了滚动条位置的标准外观(复制并非易事)
- 当我只希望它为InvalidateRect而不是ScrollWindowEx时,必须从头开始复制很多功能
由于我可以看到并发布了内部代码,因此ScrollableControl
我知道没有禁用使用的属性ScrollWindow
,但是是否有禁用使用的属性ScrollWindow
?
更新:
我尝试覆盖有问题的方法,并使用反射器窃取所有代码:
protected override void SetDisplayRectLocation(int x, int y)
{
...
Rectangle displayRect = this.displayRect;
...
this.displayRect.X = x;
this.displayRect.Y = y;
if ((nXAmount != 0) || ((nYAmount != 0) && base.IsHandleCreated))
{
...
SafeNativeMethods.ScrollWindowEx(new HandleRef(this, base.Handle), nXAmount, nYAmount, null, ref rectClip, NativeMethods.NullHandleRef, ref prcUpdate, 7);
}
...
}
问题在于SetDisplayRectLocation读取和写入私有成员变量 ( displayRect
)。除非 Microsoft 更改 C# 以允许后代访问私有成员:我不能这样做。
更新二
我意识到复制粘贴的实现ScrollableControl
,修复一个问题意味着我还必须复制粘贴整个继承链到UserControl
...
ScrollableControl2 : Control, IArrangedElement, IComponent, IDisposable
ContainerControl2 : ScrollableControl2, IContainerControl
UserControl2 : ContainerControl2
我真的更喜欢使用面向对象的设计,而不是反对它。