通过双缓冲减少闪烁:设置样式 vs. 覆盖 CreateParam

11

有人能解释一下以下概念的区别和关系吗:

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

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

谢谢!

致谢

第一个代码片段引用自MSDN页面;第二个代码片段来自如何修复用户控件中的闪烁问题,原作者为@HansPassant。


首先,应该将ControlStyles.AllPaintingInWmPaint与ControlStyles.UserPaint一起使用。话虽如此,使用这些选项的setstyle等效于this.DoubleBuffered = true;。现在的区别是setstyle应用于控件级别(例如:对于带有控件的表单,这仅适用于表单),而CreateParams将应用于表单中的所有控件,包括表单本身。 - terrybozzio
从我所了解的这个链接,它是关于范围的。 - Caramiriel
1个回答

12

感谢@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在开始时起作用,但当您调整大小时,您会失去该属性,控件再次开始闪烁。您知道有什么解决方案吗? - Tanner Ornelas
正如@TannerOrnelas所说,当您调整窗体大小时,控件会再次闪烁。 有人有解决方案吗?我尝试了很多东西,例如此帖子中的内容https://dev59.com/5HA75IYBdhLWcg3wdIv7,但我无法得到真正的解决方案。 我非常感激任何帮助。 - Ignacio
1
如同@Ignacio所说,您需要创建一个继承自Panel的类或者直接创建一个用户控件。Panel控件本身没有DoubleBuffer选项,因此使用CreateParams方法是行不通的,因为面板不会继承DoubleBuffered属性。在您的派生类或用户控件构造函数中,添加以下代码:SetStyle(ControlStyles.ResizeRedraw | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor, true); 然后在这个面板上进行绘制即可。 - Smitty-Werben-Jager-Manjenson
谢谢!创建一个UserControl并在构造函数中使用SetStyle对我很有帮助。现在,在我的新UserControl上绘制形状时不再出现闪烁了。 - Smitty-Werben-Jager-Manjenson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接