.net 如何阻止用户控件(nee ScrollableControl)调用ScrollWindow?
NET(从中派生)必须能够显示水平和垂直滚动条 调用者可以设置这些水平和垂直滚动条的可见性和范围:.net 如何阻止用户控件(nee ScrollableControl)调用ScrollWindow?,.net,scroll,scrollable,.net,Scroll,Scrollable,NET(从中派生)必须能够显示水平和垂直滚动条 调用者可以设置这些水平和垂直滚动条的可见性和范围: UserControl.AutoScroll = true; UserControl.AutoScrollMinSize = new Size(1000, 4000); //1000x4000 scroll area 注意:用户控件(即ScrollableControl)使用Windows标准机制指定WS\u HSCROLL和WS\u VSCROLL窗口样式以显示滚动条。也就是说:它们不创建单独
UserControl.AutoScroll = true;
UserControl.AutoScrollMinSize = new Size(1000, 4000); //1000x4000 scroll area
注意:用户控件(即ScrollableControl
)使用Windows标准机制指定WS\u HSCROLL
和WS\u VSCROLL
窗口样式以显示滚动条。也就是说:它们不创建单独的窗口或.NET滚动控件,而是将它们定位在窗口的右/底部。Windows有一个标准机制来显示一个或两个滚动条
如果用户滚动控件,UserControl
将发送WM\u HSCROLL
或WM\u 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
)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不会触发整个客户端矩形上的无效设置,而是尝试“修复”客户端区域中的现有内容。例如,用户向上滚动,ScrollWindowEx
将当前客户端内容向下滚动,然后只有新打开的区域无效,从而触发WM_PAINT
:
在上图中,棋盘格区域是无效的内容,必须在下一次WM_绘制期间绘制
在我的情况下,这是不好的;控件顶部包含一个“标题”(例如listview列标题)。向下滚动此内容不正确:
它会导致视觉上的破坏
我希望ScrollableControl不使用ScrollWindowEx,而是使整个客户端区域无效
我尝试重写OnScroll
protected方法:
protected override void OnScroll(ScrollEventArgs se)
{
base.OnScroll(se);
this.Invalidate();
}
但它会导致双重抽签
注意:我可以使用双缓冲来掩盖问题,但这不是真正的解决方案
- 在远程桌面/终端会话下不应使用双缓冲
- 这是对CPU资源的浪费
- 这不是我要问的问题
控件
而不是用户控件
(即继承链中的可滚动控件
之前),并手动添加HScroll或VScroll.NET控件-但这也不可取:
- Windows已经为滚动条的位置提供了一个标准的外观(复制并不容易)
- 当我只希望它无效安装时,而不是ScrollWindowEx
ScrollableControl
内部的代码,我知道没有任何属性可以禁用ScrollWindow
,但是是否有属性可以禁用ScrollWindow
的使用
更新: 我试图重写有问题的方法,并使用reflector窃取所有代码:
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);
}
...
}
问题是读取和写入私有成员变量(displayRect
)。除非微软改变C#以允许后代访问私有成员:否则我不能这样做
更新二 我意识到复制粘贴
ScrollableControl
的实现,修复一个问题意味着我还必须将整个继承链复制粘贴到UserControl
...
ScrollableControl2 : Control, IArrangedElement, IComponent, IDisposable
ContainerControl2 : ScrollableControl2, IContainerControl
UserControl2 : ContainerControl2
我真的更喜欢使用面向对象的设计,而不是反对它 你试过与微软的程序员联系吗?我相信,如果你联系微软,你可以将你的问题发布给他们,甚至可能获得电话支持
以下是支持.NET framework的链接:。它提到,您可以通过电子邮件、电话或在线与.NET支持专业人员联系。我也遇到了同样的问题,感谢您发布此消息。我可能已经找到了解决你问题的办法。我的解决方案是重载WndProc以处理滚动消息,在调用基类处理程序时关闭重画,然后在处理消息后强制重画整个窗口。此解决方案似乎工作正常:
private void sendRedrawMessage( bool redrawFlag )
{
const int WM_SETREDRAW = 0x000B;
IntPtr wparam = new IntPtr( redrawFlag ? 1 : 0 );
Message msg = Message.Create( Handle, WM_SETREDRAW, wparam, IntPtr.Zero );
NativeWindow.FromHandle( Handle ).DefWndProc( ref msg );
}
protected override void WndProc( ref Message m )
{
switch ( m.Msg )
{
case 276: // WM_HSCROLL
case 277: // WM_VSCROLL
sendRedrawMessage( false );
base.WndProc( ref m );
sendRedrawMessage( true );
Refresh(); // Invalidate all
return;
}
base.WndProc( ref m );
}
我之所以想尝试这个方法,是因为有人建议重载WndProc,再加上你观察到不能重载SetDisplayRectLocation。我认为在UserControl处理scroll事件期间禁用WM_PAINT可能会起作用
希望这有帮助
TomTom的解决方案非常棒,但我认为这是一个进行小型优化的机会。 如果没有Tom的两个方法,例如,当我通过单击滚动条端点导致滚动时,我的onPaint会看到一个调用。 当我添加Tom的两个方法时,我的onPaint开始为 相同的滚动条位置。 对我来说,解决方案似乎是忽略最后一个SB_ENDSCROLL,即滚动操作。这样,我就不再在同一个卷轴位置看到重复的绘画了
private void sendRedrawMessage(bool redrawFlag)
{
const int WM_SETREDRAW = 0x000B;
IntPtr wparam = new IntPtr(redrawFlag ? 1 : 0);
Message msg = Message.Create(Handle, WM_SETREDRAW, wparam, IntPtr.Zero);
NativeWindow.FromHandle(Handle).DefWndProc(ref msg);
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 276: // WM_HSCROLL
case 277: // WM_VSCROLL
if ((ushort)m.WParam == 8) // SB_ENDSCROLL ignore scroll bar release
break;
sendRedrawMessage(false);
base.WndProc(ref m);
sendRedrawMessage(true);
Refresh(); // Invalidate all
return;
}
base.WndProc(ref m);
}
你能提供没有脏话的截图吗;处女的眼睛得救了,问得好。你可能已经试过很多我想做的事情了。我想你已经试图覆盖WndPrc并截获滚动消息和绘画消息了?@Pedery我考虑过了,但是我仍然需要重新实现大量的处理。我的最佳选择是简单地覆盖有问题的protected
SetDisplayLocation
方法,而不是调用base
,并复制粘贴除一行之外的整个内脏。这种解决方案的缺点是a)我更喜欢使用.NETUserControl
(