我有一个具有以下属性的 Form:

  • 背景图片
  • 具有透明背景的可滚动 PanelDock = DockStyle.Fill
  • PictureBox 带有大的 WidthHeight 显示滚动条

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

    根据我的理解,双缓冲应该消除这种情况,但即使双缓冲也是如此,也许更好一点,但在滚动时仍然是一个大问题。

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

    最佳答案

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

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

    您可能会看到它的前进方向,ScrollWindowEx()还将移动窗体的BackgroundImage绘制的像素。你可以看到。接下来你会看到优化绘制的副作用,它只重绘显示的窗口部分。所以移动的背景图像像素不会被重绘。看起来像“涂抹”效果。

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

        private void panel1_Scroll(object sender, ScrollEventArgs e) {
            panel1.Invalidate();
        }
    

    但是现在您会注意到不再优化油漆的副作用。您仍然会看到像素被移动,然后过度绘制。可见性在很大程度上取决于绘制 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);
    

    效果很好,背景图像像素现在稳定了。任何其他像素,嗯,不是那么多。另一种视觉效果,我们称之为“皱纹”。可以通过将窗口置于合成模式来消除该工件。它对整个窗口表面进行双缓冲,包括子控件:
        protected override CreateParams CreateParams {
            get {
                const int WS_EX_COMPOSITED = 0x02000000;
                var cp = base.CreateParams;
                cp.ExStyle |= WS_EX_COMPOSITED;
                return cp;
            }
        }
    

    唯一剩下的工件是这个不是很便宜的代码的副作用。滚动时它可能看起来不那么平滑。否则就会告诉你为什么 28 年前 window 被设计成不透明的。

    关于c# - WinForms带有背景图像的分层控件在滚动时会导致撕裂,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32246132/

    10-17 02:00