C#Winform频繁刷新致使界面闪烁解决方法
对于大多数应用程序,.NET Framework 提供的默认双缓冲将提供最佳效果。默认状况下,标准 Windows 窗体控件是双缓冲的。能够经过两种方法对窗体和所创做的控件启用默认双缓冲。一种方法是将 DoubleBuffered 属性设置为 true,另外一种方法是经过调用 SetStyle 方法将 OptimizedDoubleBuffer 标志设置为 true。两种方法都将为窗体或控件启用默认双缓冲并提供无闪烁的图形呈现。建议仅对已为其编写全部呈现代码的自定义控件调用 SetStyle 方法。html
在构造函数里加上如下代码:c#
1 this.DoubleBuffered = true;//设置本窗体 2 SetStyle(ControlStyles.UserPaint, true); 3 SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. 4 SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲 5 //SetStyle(ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); 6 7 //UpdateStyles();
2、C#控件的闪烁问题解决方法总结
最近对代码做了一些优化,试验后效果还能够,可是发现界面会闪烁,具体是TreeView控件会闪烁,语言为C#,IDE为VS2005。在查阅一些资料,使用了一些基本技术后(如开启双缓冲),发现没什么效果。
因而使用Profiler工具,查找出瓶颈在于每次更新完界面的EndUpdate操做(使用这个是为了减小界面更新次数,但这里不理想是由于控件中中的元素不少),猜测大概每次更新,.Net底层都会更新重绘每一个图元,因此速度会慢,形成闪烁。可是若是这样,使用双缓冲应该会有较好效果。再看代码,发现多是更新动做太过频繁,因而下降速度,有所好转,但仍是不行。
继续在网上查阅,最终找到一个方案比较合适。原来底层重绘每次会清除画布,而后再所有从新绘制,这才是致使闪烁最主要的缘由。因而重载消息发送函数操做,禁掉这条消息。代码以下:多线程
1 protected override void WndProc(ref Message m) 2 { 3 if (m.Msg == 0x0014) // 禁掉清除背景消息 4 return; 5 base.WndProc(ref m); 6 }
成功!ide
注:双缓冲仍是有用的,在更新不是很频繁且控件内含元素不是特别多的时候。一旦元素过多,每次更新时间都比较长,即使使用了双缓冲,仍解决不了闪烁问题。我的认为最终比较理想的方法仍是禁掉清除背景消息。函数
附:一些尝试过但失败的记录
1)使用setStyle
网上有说使用setStyle函数去设置该控件的参数,具体为:
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
这三个选项参数后者是依赖前者的,必须并存,不然无效。而且这个函数自己是protected的,因此首先须要继承某控件再使用。
这个目标是跟前面正确解决方案一致,也是禁止清除背景并开启双缓冲,但须要使用用户绘制选项,并且是所有交由用户绘制。这须要本身实现控件的所有绘制,比较麻烦。因此这个方法不是彻底不可行,可是须要额外工做量,不推荐。我也没有使用。工具
2)使用BeginUpdate和EndUpdate
这一对操做对于须要批量操做更新控件的情景有比较好的效果,好比初始化时批量添加了大量节点。坏处就在于不能即时更新。因此,对于频繁的更新节点并但愿当即反映到界面的状况不适用。若是使用而且没有禁掉清除界面消息的话,则控件看起来就会不停的闪烁,并且以白底为主,内容几乎不可见(这个视频繁程度而定)。由于界面更新都在EndUpdate处完成,操做太多致使EndUpdate阻塞时间过长,且清空在先,更新在后,致使界面看起来长时间处于空白状态。post
3)使用ControlStyles.EnableNotifyMessage选项
这个选项的做用和正确解决方案也是一致的。使用方法是:
SetStyle(ControlStyles.EnableNotifyMessage, true);
protected override void onNotifyMessage(Message m)
{
// 此处书写过滤消息代码
}
可是实际实验显示无效果,不知是什么缘由,没有细究。性能
3、我的在一个winfrom中测试利用timer控件对要刷新的控件进行定时刷新,可能也能起到做用。测试
4、C# winform 局部刷新
作winform界面程序时,常常会遇到后台处理占用大量时间的状况,这就会形成界面假死状态。通常解决界面假死有两种方式:要么把占用大量时间的处理方式放入其余线程;要么把界面显示放入其余线程。第一种方式应该比较简单,开单独的线程,处理数据,将处理数据显示到界面就好。可是咱们常常须要在主程序运算一些内容,不然可能会改动比较大。所以,这里讲讲第二种方式。
一样是使用多线程,可是c#在其余线程刷新有一点点问题,即不能跨线程操做界面。这可使用控件的Invoke方法解决:优化
1 private delegate void CrossThread(); 2 Control control = ....; 3 CrossThread cross = delegate() 4 { 5 control.Refresh(); 6 }; 7 control.Invoke(cross);
这样可让控件在其它线程刷新界面。
再加上开新线程后的通用方法:
1 private void InvaliateControl(Control control) 2 { 3 Thread t = new Thread( 4 new ThreadStart(delegate() 5 { 6 CrossThread cross = delegate() 7 { 8 control.Refresh(); 9 }; 10 control.Invoke(cross); 11 } 12 )); 13 }
这样就能够在任什么时候候,调用此方法对控件进行刷新,而不将整个界面刷新。若是对于同一个控件,连续屡次刷新,能够添加一个成员变量做为标记,以避免同一控件连续屡次刷新,提高部分性能。
补充:在主线程调用耗时操做用此方法可能会有问题,通过验证调用Invoke函数,实际上是在主线程刷新界面。
原文连接:http://blog.csdn.net/weixin_40976261/article/details/78517409