Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 如何阻止用户控件(nee ScrollableControl)调用ScrollWindow?_.net_Scroll_Scrollable - Fatal编程技术网

.net 如何阻止用户控件(nee ScrollableControl)调用ScrollWindow?

.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窗口样式以显示滚动条。也就是说:它们不创建单独

NET(从中派生)必须能够显示水平和垂直滚动条

调用者可以设置这些水平和垂直滚动条的可见性和范围:

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可能会起作用

希望这有帮助


Tom

Tom的解决方案非常棒,但我认为这是一个进行小型优化的机会。 如果没有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)我更喜欢使用.NET
UserControl
(