Visual C#表单更新导致闪烁结果

9

我有一个用c#编写的.net应用程序,在一些表单上频繁更新显示字段。在某些情况下,表单上的每个字段(文本框、标签、图片框等)都会更改其值。而且更改的频率可能每秒钟一次。然而,当前每次更新表单时都会出现可怕的闪烁。我该如何停止闪烁?是否有一种方法可以使用双缓冲区?请帮忙!

10个回答

7

简短的回答是

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

长话短说:请查看MSDN或者google。仅供娱乐,尝试在每个元素更新后调用Application.DoEvents(),看看问题会变得更好还是更糟;-)

我不确定那会有帮助。听起来问题在于标准控件在闪烁,而OptimizedDoubleBuffer只能帮助自定义渲染的控件。 - Cameron MacFarland

6
这对我很有帮助。
基本上,它涉及从所需的控件派生并设置以下样式。 http://www.syncfusion.com/faq/windowsforms/search/558.aspx
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.DoubleBuffer, true); 

这应该是答案! - RendoJack

4

我知道这个问题已经很久了,但也许将来还有人会搜索到它。

双缓冲并不总是起作用的。如果要强制窗体根本不闪烁(但有时可能会导致绘图问题):

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000; //WS_EX_COMPOSITED
        return cp;
    }
}

为了防止用户调整窗体大小时出现闪烁,但不会影响控件的绘制(假设你的窗体名称为 "Form1"),请按以下步骤操作:
int intOriginalExStyle = -1;
bool bEnableAntiFlicker = true;

public Form1()
{
    ToggleAntiFlicker(false);
    InitializeComponent();
    this.ResizeBegin += new EventHandler(Form1_ResizeBegin);
    this.ResizeEnd += new EventHandler(Form1_ResizeEnd);
}

protected override CreateParams CreateParams
{
    get
    {
        if (intOriginalExStyle == -1)
        {
            intOriginalExStyle = base.CreateParams.ExStyle;
        }
        CreateParams cp = base.CreateParams;

        if (bEnableAntiFlicker)
        {
            cp.ExStyle |= 0x02000000; //WS_EX_COMPOSITED
        }
        else
        {
            cp.ExStyle = intOriginalExStyle;
        }

        return cp;
    }
} 

private void Form1_ResizeBegin(object sender, EventArgs e)
{
    ToggleAntiFlicker(true);
}

private void Form1_ResizeEnd(object sender, EventArgs e)
{
    ToggleAntiFlicker(false);
}

private void ToggleAntiFlicker(bool Enable)
{
    bEnableAntiFlicker = Enable;
    //hacky, but works
    this.MaximizeBox = true;
}

这是最好的答案,也是我认为最权威的答案。 - HydroPowerDeveloper

4

在开始更新之前,您可以尝试调用this.SuspendLayout();,并在设置完所有值后使用this.ResumeLayout(false);,这样做可以防止表单逐个写入值。


1
suspendlayout/resumelayout用于抑制与添加和移动控件相关的事件。 - Steven A. Lowe
1
正确,事件仅在更新大量控件组时防止调用布局函数多次。因此,这些函数将加速布局但不会防止闪烁。与双缓冲一起使用将产生最佳效果。 - Toji

2

您可以将原始控件替换为自定义控件,该控件具有保护的DoubleBuffered属性设置为true。例如,对于ListView,代码如下:

internal class DoubleBufferedListView : ListView {

    public DoubleBufferedListView()
        : base() {
        this.DoubleBuffered = true;
    }

}

之后,您只需访问*.Designer.cs文件,并将所有对原生控件的提及替换为此控件。

P.S. 您也可以通过反射设置此属性,而无需继承自控件:

listView1.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(lsvReport, true, null);

虽然不太干净也不建议这样做,但是它不需要对*.Designer.cs文件进行任何更改。


2
这也可能是由于您的编码问题,而不是缺少双缓冲造成的。我刚才遇到了类似的问题,但后来发现原因是:
  1. 当未选择项目时,我将框架设置为不可见。
  2. 在用户进行选择之间,ListView控件会清除索引。
  3. 我绑定了SelectedIndexChanged事件。
换句话说:
  • 用户单击项目1
    ~ SelectedIndexChanged(1)
  • 用户单击项目2
    ~ SelectedIndexChanged(-1) <---- 这导致闪烁
    ~ SelectedIndexChanged(2)
那么解决方案是什么?请参考如何避免数千个不必要的ListView.SelectedIndexChanged事件?

1

你没有做好研究。每个窗体中都有一个DoubleBuffered属性,请尝试将其设置为true。如果您没有在窗体绘图方面进行过载,则一切都应该正常工作。


谢谢!我花了很长时间寻找它,而它一直就在我面前。 - GrandMasterFlush

1

你可以将几乎所有的 Windows Forms 控件 双缓冲,尽管大多数情况下需要从所需的控件继承并重写一个受保护的属性。不过要小心,因为我在同样的问题上花了很多时间,但仍然无法完全消除我更复杂表单的闪烁。

如果你想要真正无闪烁的窗口,我建议看一下 WPF。


0
我曾经遇到过与OpenGLES相同的问题,这也是我找到这个帖子的原因。 当然,我意识到您并没有使用ogl,但也许这会对您有所帮助;) < p > < code > protected override void OnPaintBackground(PaintEventArgs e) { }


0

通常情况下,出现幽灵图像的原因是因为您正在单线程中运行,并且由于字段更新而被阻塞,因此绘制事件无法触发。解决这个问题的一种方法是将繁重的工作放在异步方法中。这将允许窗体重新绘制自身并在异步方法回调时更新所需的任何内容。


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