通过双缓冲区减少闪烁:SetStyle 与覆盖 CreateParam

Posted

技术标签:

【中文标题】通过双缓冲区减少闪烁:SetStyle 与覆盖 CreateParam【英文标题】:To reduce flicker by double buffer: SetStyle vs. overriding CreateParam 【发布时间】:2014-11-10 10:38:58 【问题描述】:

谁能解释一下两者的区别和关系

SetStyle(ControlStyles.UserPaint |
         ControlStyles.AllPaintingInWmPaint |
         ControlStyles.DoubleBuffer, true)

protected override CreateParams CreateParams

    get
    
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
        return cp;
    

它们是减少闪烁所必需的,但何时以及如何正确使用它们?可以单独使用,还是必须成对使用,这是什么原因?

谢谢!

学分

第一个代码sn-p引用自MSDN page;第二个代码sn-p在How to fix the flickering in User controls上找到,原作者是@HansPassant。

【问题讨论】:

first - ControlStyles.AllPaintingInWmPaint 应该与 ControlStyles.UserPaint 一起使用,已经说过带有这些选项的 setstyle 相当于 this.DoubleBuffered = true;。现在不同的是 setstyle 应用于控件级别(例如:带有控件的表单,这将仅应用于表单),而 CreateParams 将应用于表单中的所有控件,包括表单。 csharptutorial.in/15/csharp-how-to-avoid-flickering-in-csharp - 从我从这个链接得到的关于范围的信息。 【参考方案1】:

感谢@terrybozzlo 的解释和@Caramiriel 澄清问题的精彩页面。

我想总结一下我在这里得到的一切。


为什么会出现闪烁

当您的表单或容器控件(例如 Panel)包含太多控件时(以及默认情况下打开 WS_CLIPCHILDREN 时),通常会发生闪烁。根据@HansPassant:

它绘制 BackgroundImage,在子控件窗口所在的位置留下孔。然后每个子控件都会收到一条消息来绘制自己,他们将用他们的窗口内容填充这个洞。当你有很多控件时,这些漏洞对用户来说是可见的一段时间。它们通常是白色的,与背景图像在黑暗时形成鲜明对比。或者,如果表单设置了 Opacity 或 TransparencyKey 属性,它们可能是黑色的,与几乎任何东西形成鲜明对比。

如何在控制级别避免它们

您应该将控件的DoubleBuffered 属性设置为true。为此,您需要从基本类型派生控件(如果它不是用户控件)并在构造函数中进行设置。

例如,要获得Panel 双缓冲,您需要这样做:

public class BufferedPanel : Panel

    public BufferedPanel()
    
        DoubleBuffered = true;
    

或者,您可以使用:

SetStyle(ControlStyles.UserPaint |
         ControlStyles.AllPaintingInWmPaint |
         ControlStyles.DoubleBuffer, true);

为了获得相同的效果,即它们是等价的

如何在表单级别避免它们

上述技术将减少控件级别的闪烁,这意味着当重新绘制表单时,所有控件将不再闪烁。但最终的解决方案是从表单层面减少闪烁:当表单被重绘时,表单及其所有子项都被双缓冲。

这需要覆盖CreateParams:

protected override CreateParams CreateParams

    get
    
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
        return cp;
    

总结

SetStyle在控件层做工作,CreateParam在表单层做工作,实现表单内所有控件的双缓冲。

学分:

@terrybozzlo、@Caramiriel、@HansPassant

【讨论】:

...在子控制窗口所在的位置留下孔。不会。当您设置 WS_CLIPCHILDREN 时会发生这种情况,否则它会绘制到包括子控件在内的整个区域。 SETSTYLE 不会消除闪烁,也不能仅用于整个表单的特定控件。 CreateParams 在开始时有效,但是当您调整大小时,您会失去该属性并且控件再次开始闪烁。你知道解决这个问题的方法吗? 正如@TannerOrnelas 所说,当您调整表单大小时,控件再次开始闪烁。有没有人有办法解决吗?我已经尝试了很多事情,比如这篇文章***.com/questions/3286373/… 中的事情,但我无法得到真正的解决方案。我非常感谢任何帮助。 @Ignacio 正如这个答案所述,您需要创建自己的从 Panel 派生的类,或者只是创建一个用户控件。 Panel 控件按原样没有 DoubleBuffer 选项,因此使用CreateParams 方法将不起作用,因为面板不会继承 DoubleBuffered 属性。在您的派生类或 UserControl 构造函数中,添加以下 SetStyle(ControlStyles.ResizeRedraw | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor, true); 在此面板上绘制。 谢谢!创建一个 UserControl 并在构造函数中使用 SetStyle 对我有用。在我的新 UserControl 上绘制形状时不再闪烁。

以上是关于通过双缓冲区减少闪烁:SetStyle 与覆盖 CreateParam的主要内容,如果未能解决你的问题,请参考以下文章

Java中用双缓冲技术消除闪烁

在 MFC CPaintDC 中进行双缓冲后闪烁不会减少

怎么解决Winform里dataGridView放在计时器里滚动条闪烁问题

#使用ListView更新数据出现闪烁解决办法

winform的双缓冲

用C#listview控件Details类型,发现当拉动表头调整列宽度过程时,listview会重画,造成不停的闪烁