Solve the problem of flickering form background in Winform application

Solve the problem of flickering form background in Winform application

Recently, some optimizations have been made to the code, and the effect is not bad after the test, but the interface will flicker, specifically the TreeView control will flicker, the language is C#, and the IDE is VS2005. After consulting some information and using some basic techniques (such as turning on double buffering), I found that it has no effect.

    于是使用Profiler工具,查找出瓶颈在于每次更新完界面的EndUpdate操作(使用这个是为了减少界面更新次数,但这里不理想是因为控件中中的元素很多),猜想大概每次更新,.Net底层都会更新重绘每个图元,所以速度会慢,造成闪烁。但是如果这样,使用双缓冲应该会有较好效果。再看代码,发现可能是更新动作太过频繁,于是降低速度,有所好转,但还是不行。

   继续在网上查阅,最终找到一个方案比较合适。原来底层重绘每次会清除画布,然后再全部重新绘制,这才是导致闪烁最主要的原因。于是重载消息发送函数操作,禁掉这条消息。代码如下:

protected override void WndProc(ref Message m)

{

    if (m.Msg == 0x0014) // 禁掉清除背景消息

        return;

    base.WndProc(ref m);

}

    成功!

Note: Double buffering is still useful, when the update is not very frequent and the control contains not too many elements. Once there are too many elements, each update takes a long time. Even if double buffering is used, the problem of flickering cannot be solved. Personally, I think the final ideal method is to disable and clear background messages.

Attached: Some records that have been tried but failed

1) Use setStyle

  网上有说使用setStyle函数去设置该控件的参数,具体为:

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

  这三个选项参数后者是依赖前者的,必须并存,否则无效。并且这个函数本身是protected的,所以首先需要继承某控件再使用。

  这个目标是跟前面正确解决方案一致,也是禁止清除背景并开启双缓冲,但需要使用用户绘制选项,而且是全部交由用户绘制。这需要自己实现控件的全部绘制,比较麻烦。所以这个方法不是完全不可行,但是需要额外工作量,不推荐。我也没有使用。

2) Use BeginUpdate and EndUpdate

  这一对操作对于需要批量操作更新控件的情景有比较好的效果,比如初始化时批量添加了大量节点。坏处就在于不能即时更新。所以,对于频繁的更新节点并希望立即反映到界面的情况不适用。如果使用并且没有禁掉清除界面消息的话,则控件看起来就会不停的闪烁,而且以白底为主,内容几乎不可见(这个视频繁程度而定)。因为界面更新都在EndUpdate处完成,操作太多导致EndUpdate阻塞时间过长,且清空在先,更新在后,导致界面看起来长时间处于空白状态。

3) Use the ControlStyles.EnableNotifyMessage option

  这个选项的作用和正确解决方案也是一致的。使用方法是:

  SetStyle(ControlStyles.EnableNotifyMessage, true);

  protected override void onNotifyMessage(Message m)

  {

           // 此处书写过滤消息代码

  }

  但是实际实验显示无效果,不知是什么原因,没有细究。

My operating system is Win7, and the VS version I use is VS2012. The codes in this article are all C# codes.

I have encountered a problem in the past few days, that is, I use a Panel with embedded pictures as the background of the Winform application, as shown in the following figure:

This is a Winform form with a Panel placed in it, the Dock property is Fill, the BackgroundImage uses the cover picture of the 2003 issue 02 of "Youth Electric World", and the BackgroundImageLayout uses Stretch.

There are now two problems with this interface:

1. When the form is opened for the first time, the background image will flicker obviously

2. When pulling the border of the form to resize the form, the background image does not appear to flicker obviously

In order to deal with this problem, I checked some information and tried them one by one. Let me talk about two representative methods:

Method 1: Use double buffering directly

SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); // Disable erasing the background.
SetStyle(ControlStyles.DoubleBuffer, true); // Double buffering
I try to add this code to the form In the constructor, it does not solve the problem, the flickering is still very obvious

There is also an article on MSDN "How to reduce graphics flicker by using double buffering for forms and controls"

Address: https://msdn.microsoft.com/zh-cn/library/3t7htc9c%28v=vs.80%29.aspx

This article also introduces a method using double buffering:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
This method still cannot solve the problem.

Method 2: Override the CreateParams method

Method 2 needs to put the following piece of code in the code of the Form class:

protected override CreateParams CreateParams
{
    
    
    get
    {
    
    
        CreateParams paras = base.CreateParams;
        paras.ExStyle |= 0x02000000;用双缓冲绘制窗口的所有子控件
        return paras;
    }


}

I thought this method was effective when I first tried it, but after using it for a while, I still found a problem:

1. This method can solve problem 1, but not problem 2

2. This method will affect the redrawing of some other controls and components (this is fatal)

Therefore, this method cannot solve the problem either.

Neither of the above two methods could solve the problem, so I continued to seek help from Du Niang, and finally found a solution on the following page:

Method 3: Encapsulate the Panel class

http://blog.chinaunix.net/uid-14414741-id-2814313.html

In this method, you need to create a new PanelEnhanced class to inherit the Panel class, the code is as follows:

/// <summary>
/// 加强版 Panel
/// </summary>
class PanelEnhanced : Panel
{
    
    
    /// <summary>
    /// OnPaintBackground 事件
    /// </summary>
    /// <param name="e"></param>
    protected override void OnPaintBackground(PaintEventArgs e)
    {
    
    
        // 重载基类的背景擦除函数,
        // 解决窗口刷新,放大,图像闪烁
        return;
    }
 
    /// <summary>
    /// OnPaint 事件
    /// </summary>
    /// <param name="e"></param>
    protected override void OnPaint(PaintEventArgs e)
    {
    
    
        // 使用双缓冲
        this.DoubleBuffered = true;
        // 背景重绘移动到此
        if (this.BackgroundImage != null)
        {
    
    
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            e.Graphics.DrawImage(
                this.BackgroundImage,
                new System.Drawing.Rectangle(0, 0, this.Width, this.Height),
                0,
                0,
                this.BackgroundImage.Width,
                this.BackgroundImage.Height,
                System.Drawing.GraphicsUnit.Pixel);
        }
        base.OnPaint(e);
    }
}

Replace the Panel container in the form we created before with our newly packaged PanelEnhanced container, put the background picture of the program in it, and then run the program, the problem of program background flickering is perfectly solved!

END

Guess you like

Origin blog.csdn.net/kalvin_y_liu/article/details/128136015