WinForms分层控件与背景图像导致滚动时撕裂

Ron*_*nny 3 .net c# winforms

我有Form以下属性:

  • 背景图片
  • 可滚动Panel透明背景,和Dock = DockStyle.Fill
  • PictureBox用大WidthHeight,显示滚动条

现在所有控件都设置为DoubleBuffered,包括表单本身.一切都按预期工作,除了滚动PictureBox的面板时,表单背景图像滚动,它重复显示垂直和水平撕裂,虽然它的静态图像符合表单的大小,当你停止滚动它显示正确.这仅在拖动滚动条时发生,如果我单击滚动条中的任何一点来移动它,它会正确显示.

根据我的理解,Double Buffering应该消除这种情况,但即使使用双缓冲也是如此,可能稍微好一点,但滚动时仍然是一个巨大的问题.

我试图将所有控件放在另一个面板中,而不是使用表单背景图像,并将此面板放在窗体上,但它没有任何区别.

Han*_*ant 11

您正在与Windows系统选项进行战斗,名为"拖动时显示窗口内容".它为所有现代版本的Windows启用.关闭它不是一个现实的目标,因为它是一个系统选项,它会影响所有应用程序的所有窗口.没有后门有选择地绕过这个选项.

启用它后,操作系统会优化窗口的滚动.它执行快速bitblt以移动视频帧缓冲区中的像素,并仅为滚动显示的窗口部分生成绘制消息.向下滚动时,就像底部几行像素一样.底层的winapi调用是ScrollWindowEx().意图是为应用程序提供响应更快的UI,实现滚动所需的工作量要少得多.

您可以看到它的前进方向,ScrollWindowEx()也会移动由窗体的BackgroundImage绘制的像素.你可以看到.接下来你看到的是优化涂料的副作用,它只重绘了所显示的窗口部分.因此移动的背景图像像素不会重绘.看起来像"涂抹"效果.

有一个简单的解决方法,只需为面板的Scroll事件实现一个事件处理程序并调用Invalidate().所以整个面板再次重绘:

    private void panel1_Scroll(object sender, ScrollEventArgs e) {
        panel1.Invalidate();
    }
Run Code Online (Sandbox Code Playgroud)

但现在你会注意到油漆的副作用不再被优化.你仍然看到像素被移动,然后透支.这有多可见取决于BackgroundImage的绘制成本.通常从不便宜,因为它没有最佳的像素格式(32bppPArgb),并且没有合适的尺寸,因此需要重新调整以适应窗口.视觉效果类似于"pogo",在面板的一个边缘上快速抖动.

您不太可能发现可接受或想要完成优化BackgroundImage的工作.阻止ScrollWindowEx()执行其工作需要一个非常大的武器,你可以调整LockWindowUpdate().像这样:

 using System.Runtime.InteropServices;
 ...
    private void panel1_Scroll(object sender, ScrollEventArgs e) {
        if (e.Type == ScrollEventType.First) {
            LockWindowUpdate(this.Handle);
        }
        else {
            LockWindowUpdate(IntPtr.Zero);
            panel1.Update();
            if (e.Type != ScrollEventType.Last) LockWindowUpdate(this.Handle);
        }
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool LockWindowUpdate(IntPtr hWnd);
Run Code Online (Sandbox Code Playgroud)

效果很好,背景图像像素现在稳定.任何其他像素,嗯,不是那么多.另一种视觉效果,让我们称之为"皱纹".通过将窗口置于合成模式可以完成摆脱该工件.哪个双缓冲整个窗口表面,包括子控件:

    protected override CreateParams CreateParams {
        get {
            const int WS_EX_COMPOSITED = 0x02000000;
            var cp = base.CreateParams;
            cp.ExStyle |= WS_EX_COMPOSITED;
            return cp;
        }
    }
Run Code Online (Sandbox Code Playgroud)

只有剩下的工件是这不是非常便宜的代码的副作用.滚动时,它可能看起来不那么平滑.否则会告诉你为什么28年前窗户被设计成不透明的.

  • 完美的工作,感谢详细的解释和解决方案:) (2认同)