如何阻止UserControl(nee ScrollableControl)调用ScrollWindow? [英] How to stop a UserControl (nee ScrollableControl) from calling ScrollWindow?

查看:86
本文介绍了如何阻止UserControl(nee ScrollableControl)调用ScrollWindow?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

.NET 可以读写私有文件成员变量( displayRect )。除非Microsoft更改C#以允许后代访问私有成员:否则我将无法这样做。






更新两次



我意识到,复制粘贴 ScrollableControl 的实现,修复一个 issue 意味着我也将拥有将整个继承链复制n粘贴到 UserControl

  ... 
ScrollableControl2:控件,IArrangedElement,IComponent,IDisposable
ContainerControl2:ScrollableControl2,IContainerControl
UserControl2:ContainerControl2

我真的更愿意与面向对象的设计一起工作,而不是反对。

解决方案

我也遇到了同样的问题,谢谢发布。我可能已经找到解决您问题的方法。我的解决方案是重载WndProc以处理滚动消息,在调用基类处理程序时关闭重绘,然后在处理完消息后强制重绘整个窗口。此解决方案似乎可以正常工作:

  private void sendRedrawMessage(bool redrawFlag)
{
const int WM_SETREDRAW = 0x000B;

IntPtr wparam = new IntPtr(redrawFlag?1:0);
消息msg = Message.Create(句柄,WM_SETREDRAW,wparam,IntPtr.Zero);
NativeWindow.FromHandle(Handle).DefWndProc(ref msg);
}

受保护的覆盖无效WndProc(ref Message m)
{
switch(m.Msg)
{
case 276:/ / WM_HSCROLL
case 277:// WM_VSCROLL
sendRedrawMessage(false);
base.WndProc(ref m);
sendRedrawMessage(true);
Refresh(); //使所有
回报无效;
}

base.WndProc(ref m);
}

我之所以尝试此操作,是因为建议使WndProc超载并结合您的观察您不能重载SetDisplayRectLocation。我认为在UserControl处理滚动事件期间禁用WM_PAINT可能有效。



希望这会有所帮助。



汤姆


The .NET UserControl (which descends from ScrollableControl) has to ability to display horizontal and vertical scrollbars.

The caller can set the visibility, and range, of these horizontal and vertical scrollbars:

UserControl.AutoScroll = true;
UserControl.AutoScrollMinSize = new Size(1000, 4000); //1000x4000 scroll area

Note: The UserControl (i.e. ScrollableControl) uses the Windows standard mechanism of specifying WS_HSCROLL and WS_VSCROLL window styles to make scrollbars appear. That is: they do not create separate Windows or .NET scroll controls, positioning them at the right/bottom of the window. Windows has a standard mechanism for displaying one, or both, scrollbars.

If the user scrolls the control, the UserControl is sent a WM_HSCROLL or WM_VSCROLL message. In response to these messages i want the ScrollableControl to invalidate the client area, which is what would happen in native 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;
 }

i need the entire client area invalidated. The problem is that UserControl (i.e. ScrollableControl) calls the ScrollWindow API function:

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);
    }
    ...
}

Rather than triggering an InvalidateRect on the entire client rectangle, ScrollableControl tries to "salvage" the existing content in the client area. For example, the user scrolls up, the current client content is pushed down by ScrollWindowEx, and then only the newly uncovered area is invalidated, triggering a WM_PAINT:

In the above diagram, the checkerboard area is the content that is invalid and will have to be painted during the next WM_PAINT.

In my case this is no good; the top of my control contains a "header" (e.g. listview column headers). Scrolling this content further down is incorrect:

and it causes visual corruption.

i want the ScrollableControl to not use ScrollWindowEx, but instead just invalidate the entire client area.

i tried overriding OnScroll protected method:

protected override void OnScroll(ScrollEventArgs se)
{
   base.OnScroll(se);

   this.Invalidate();
}

But it causes an double-draw.

Note: i could use double-buffering to mask the problem, but that's not a real solution

  • double buffering should not be used under remote desktop/terminal session
  • it's wasteful of CPU resources
  • it's not the question i'm asking

i considered using a Control instead of UserControl (i.e. before ScrollableControl in the inheritance chain) and manually add a HScroll or VScroll .NET control - but that's not desirable either:

  • Windows already provides a standard look for the position of scrollbars (it's not trivial to duplicate)
  • that is a lot of functionality to have to reproduce from scratch, when i only want it to InvalidateRect rather than ScrollWindowEx

Since i can see, and posted, the code internal to ScrollableControl i know there is no property to disable use of ScrollWindow, but is there a property to disable the use of ScrollWindow?


Update:

i tried overriding the offending method, and using reflector to steal all the code:

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);
    }
    ...
}

The problem is that SetDisplayRectLocation reads and writes to a private member variable (displayRect). Unless Microsoft changes C# to allow descendants access to private members: i cannot do that.


Update Two

i realized that copy-pasting the implementation of ScrollableControl, fixing the one issue means i will also have to copy-n-paste the entire inheritance chain down to UserControl

...
   ScrollableControl2 : Control, IArrangedElement, IComponent, IDisposable
      ContainerControl2 : ScrollableControl2, IContainerControl
         UserControl2 : ContainerControl2

i'd really prefer to work with object-oriented design, rather than against it.

解决方案

I had the same problem, thanks for posting this. I may have found a solution to your problem. My solution is to overload WndProc in order to handle the scroll messages, turn off redraw while calling the base class handler, then force a redraw of the entire window after the message has been handled. This solution appears to work ok:

    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 );
    }

I thought of trying this because of the suggestion to overload WndProc combined with your observation that you can't overload SetDisplayRectLocation. I thought that disabling WM_PAINT during the UserControl's handling of the scroll event might work.

Hope this helps.

Tom

这篇关于如何阻止UserControl(nee ScrollableControl)调用ScrollWindow?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆