视觉c#表单更新导致闪烁

本文关键字:闪烁 更新 表单 视觉 | 更新日期: 2023-09-27 17:47:47

我有一个用c#编写的.net应用程序。在某些表单上,我经常更新显示字段。在某些情况下,表单上的每个字段(文本框、标签、图片框等)都会更改其值。此外,变化的频率可能是每秒一次。然而,目前每次更新表单时都会出现可怕的闪烁。我怎样才能停止闪烁?有没有办法让缓冲区加倍?请帮忙!

视觉c#表单更新导致闪烁

简短的答案是

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

答案很长:请参阅MSDN或谷歌

为了好玩,在每个元素更新后尝试调用Application.DoEvents(),看看问题是好是坏;-)

这对我有效。

http://www.syncfusion.com/faq/windowsforms/search/558.aspx

基本上,它涉及从所需的控件派生并设置以下样式。

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

您可以尝试调用this.SuspendLayout()在您开始更新之前,并且此。ResumeLayout(false)以这种方式设置完所有值后,应该会阻止表单一次写入一个值。

我知道这个问题已经过时了,但将来其他人可能会搜索它。

DoubleBuffering并不总是能很好地工作。强制表单从不闪烁(但有时会导致绘图问题):

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

您可以用自定义控件替换原始控件,该控件已将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文件进行任何更改。

这也可能是由您的编码引起的,而不是没有双缓冲。我刚刚带着类似的问题来到这里,但意识到这是因为:

  1. 当项目未被选中时,我将框架设置为不可见
  2. 在用户选择之间,ListView控件会清除索引
  3. 我已绑定到SelectedIndexChanged事件

换句话说:

  • 用户单击项目1
    ~选定的索引已更改(1)
  • 用户单击项目2
    ~ SelectedIndexChanged(-1)<----这会导致闪烁
    ~选定的索引已更改(2)

那么解决方案是什么呢?如何避免成千上万不必要的ListView.SelectedIndexChanged事件?

你研究得不好。每个窗体中都有一个DoubleBuffered属性。请尝试将其设置为true。如果你没有在形式绘画上超载任何东西,那么一切都应该正常。

您几乎可以对每个windows窗体控件进行双重缓冲,尽管大多数时候需要从所需控件继承并重写受保护的属性。不过,请注意,我在同一个问题上花了很多时间,我还没有完全消除我更复杂的表单上的闪烁。

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

重影通常是由于在单个线程中运行,并且由于字段更新而被搁置,因此绘制事件不会触发。解决这个问题的一种方法是将繁重的工作放在异步方法中。这将允许表单重新绘制自己,并在异步方法回调时更新所需的内容。

我在OpenGLES中遇到了同样的问题,这就是我找到这个线程的原因。当然,我意识到你没有使用ogl,但也许这对你有帮助;)

protected override void OnPaintBackground(PaintEventArgs e) { }