首先我們需要重新創(chuàng)建一個(gè)Panel類,其繼承系統(tǒng)自帶的Panel類,然后充新寫一個(gè)構(gòu)造函數(shù),對(duì)其中的部分樣式進(jìn)行更改。
代碼:
public class NewPanel:Panel { public NewPanel() { this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); this.SetStyle(ControlStyles.UserPaint, true); } }
然后在窗口初始化的代碼塊中更改為我們當(dāng)前新建的Panel類即可,其它代碼都不必用~。
個(gè)人認(rèn)為是非常棒的一種解決方案,可以完全解決閃爍的問題。
利用winform開發(fā)時(shí),可能都會(huì)遇到一個(gè)問題,就是在panel中不停的重繪圖形時(shí),圖形會(huì)不停的閃爍。要解決這個(gè)辦法只需要開啟雙緩沖即可,由于初學(xué)c#,理解的不是很深,所以不多做解釋。以下代碼親測(cè)可以解決這個(gè)問題:
首先創(chuàng)建一個(gè)自己的panel類:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace Test { //開啟雙緩沖 class MyPanel:Panel { public MyPanel() { SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); } } }
然后重新生成解決方法,就會(huì)發(fā)現(xiàn)工具箱中多了一個(gè)自己寫的panel控件,在畫圖形的時(shí)候,畫在自己的panel上就可以了。由于自己的panel繼承自Panel類,所以Panel中的方法都可以使用。
vb.net版本:
Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Windows.Forms Namespace Test '開啟雙緩沖 Class MyPanel Inherits Panel Public Sub New() MyBase.New SetStyle((ControlStyles.UserPaint _ Or (ControlStyles.AllPaintingInWmPaint _ Or (ControlStyles.OptimizedDoubleBuffer _ Or (ControlStyles.ResizeRedraw Or ControlStyles.SupportsTransparentBackColor)))), true) End Sub End Class End Namespace
另外技術(shù)文章說(shuō)明:
一、通過(guò)對(duì)窗體和控件使用雙緩沖來(lái)減少圖形閃爍(當(dāng)繪制圖片時(shí)出現(xiàn)閃爍時(shí),使用雙緩沖)
對(duì)于大多數(shù)應(yīng)用程序,.NET Framework 提供的默認(rèn)雙緩沖將提供最佳效果。默認(rèn)情況下,標(biāo)準(zhǔn) Windows 窗體控件是雙緩沖的。可以通過(guò)兩種方法對(duì)窗體和所創(chuàng)作的控件啟用默認(rèn)雙緩沖。一種方法是將 DoubleBuffered 屬性設(shè)置為 true,另一種方法是通過(guò)調(diào)用 SetStyle 方法將 OptimizedDoubleBuffer 標(biāo)志設(shè)置為 true。兩種方法都將為窗體或控件啟用默認(rèn)雙緩沖并提供無(wú)閃爍的圖形呈現(xiàn)。建議僅對(duì)已為其編寫所有呈現(xiàn)代碼的自定義控件調(diào)用 SetStyle 方法。
在構(gòu)造函數(shù)里加上以下代碼:
this.DoubleBuffered = true;//設(shè)置本窗體 SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. SetStyle(ControlStyles.DoubleBuffer, true); // 雙緩沖 //SetStyle(ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); //UpdateStyles();
二、C#控件的閃爍問題解決方法總結(jié)
最近對(duì)代碼作了一些優(yōu)化,試驗(yàn)后效果還可以,但是發(fā)現(xiàn)界面會(huì)閃爍,具體是TreeView控件會(huì)閃爍,語(yǔ)言為C#,IDE為VS2005。在查閱一些資料,使用了一些基本技術(shù)后(如開啟雙緩沖),發(fā)現(xiàn)沒什么效果。
于是使用Profiler工具,查找出瓶頸在于每次更新完界面的EndUpdate操作(使用這個(gè)是為了減少界面更新次數(shù),但這里不理想是因?yàn)榭丶兄械脑睾芏啵孪氪蟾琶看胃拢?Net底層都會(huì)更新重繪每個(gè)圖元,所以速度會(huì)慢,造成閃爍。但是如果這樣,使用雙緩沖應(yīng)該會(huì)有較好效果。再看代碼,發(fā)現(xiàn)可能是更新動(dòng)作太過(guò)頻繁,于是降低速度,有所好轉(zhuǎn),但還是不行。
繼續(xù)在網(wǎng)上查閱,最終找到一個(gè)方案比較合適。原來(lái)底層重繪每次會(huì)清除畫布,然后再全部重新繪制,這才是導(dǎo)致閃爍最主要的原因。于是重載消息發(fā)送函數(shù)操作,禁掉這條消息。代碼如下:
protected override void WndProc(ref Message m) { if (m.Msg == 0x0014) // 禁掉清除背景消息 return; base.WndProc(ref m); }
成功!
注:雙緩沖還是有用的,在更新不是很頻繁且控件內(nèi)含元素不是特別多的時(shí)候。一旦元素過(guò)多,每次更新時(shí)間都比較長(zhǎng),即便使用了雙緩沖,仍解決不了閃爍問題。個(gè)人認(rèn)為最終比較理想的方法還是禁掉清除背景消息。
附:一些嘗試過(guò)但失敗的記錄
1)使用setStyle
網(wǎng)上有說(shuō)使用setStyle函數(shù)去設(shè)置該控件的參數(shù),具體為:
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
這三個(gè)選項(xiàng)參數(shù)后者是依賴前者的,必須并存,否則無(wú)效。并且這個(gè)函數(shù)本身是protected的,所以首先需要繼承某控件再使用。
這個(gè)目標(biāo)是跟前面正確解決方案一致,也是禁止清除背景并開啟雙緩沖,但需要使用用戶繪制選項(xiàng),而且是全部交由用戶繪制。這需要自己實(shí)現(xiàn)控件的全部繪制,比較麻煩。所以這個(gè)方法不是完全不可行,但是需要額外工作量,不推薦。我也沒有使用。
2)使用BeginUpdate和EndUpdate
這一對(duì)操作對(duì)于需要批量操作更新控件的情景有比較好的效果,比如初始化時(shí)批量添加了大量節(jié)點(diǎn)。壞處就在于不能即時(shí)更新。所以,對(duì)于頻繁的更新節(jié)點(diǎn)并希望立即反映到界面的情況不適用。如果使用并且沒有禁掉清除界面消息的話,則控件看起來(lái)就會(huì)不停的閃爍,而且以白底為主,內(nèi)容幾乎不可見(這個(gè)視頻繁程度而定)。因?yàn)榻缑娓露荚贓ndUpdate處完成,操作太多導(dǎo)致EndUpdate阻塞時(shí)間過(guò)長(zhǎng),且清空在先,更新在后,導(dǎo)致界面看起來(lái)長(zhǎng)時(shí)間處于空白狀態(tài)。
3)使用ControlStyles.EnableNotifyMessage選項(xiàng)
這個(gè)選項(xiàng)的作用和正確解決方案也是一致的。使用方法是:
SetStyle(ControlStyles.EnableNotifyMessage, true);
protected override void onNotifyMessage(Message m)
{
// 此處書寫過(guò)濾消息代碼
}
但是實(shí)際實(shí)驗(yàn)顯示無(wú)效果,不知是什么原因,沒有細(xì)究。
三、個(gè)人在一個(gè)winfrom中測(cè)試?yán)胻imer控件對(duì)要刷新的控件進(jìn)行定時(shí)刷新,可能也能起到作用。
四、C# winform 局部刷新
做winform界面程序時(shí),經(jīng)常會(huì)遇到后臺(tái)處理占用大量時(shí)間的情況,這就會(huì)造成界面假死狀態(tài)。一般解決界面假死有兩種方式:要么把占用大量時(shí)間的處理方式放入其他線程;要么把界面顯示放入其他線程。第一種方式應(yīng)該比較簡(jiǎn)單,開單獨(dú)的線程,處理數(shù)據(jù),將處理數(shù)據(jù)顯示到界面就好。但是我們經(jīng)常需要在主程序運(yùn)算一些內(nèi)容,否則可能會(huì)改動(dòng)比較大。因此,這里講講第二種方式。
同樣是使用多線程,但是c#在其他線程刷新有一點(diǎn)點(diǎn)問題,即不能跨線程操作界面。這可以使用控件的Invoke方法解決:
private delegate void CrossThread(); Control control = ....; CrossThread cross = delegate() { control.Refresh(); }; control.Invoke(cross);
這樣可以讓控件在其它線程刷新界面。
再加上開新線程后的通用方法:
private void InvaliateControl(Control control) { Thread t = new Thread( new ThreadStart(delegate() { CrossThread cross = delegate() { control.Refresh(); }; control.Invoke(cross); } )); }
這樣就可以在任何時(shí)候,調(diào)用此方法對(duì)控件進(jìn)行刷新,而不將整個(gè)界面刷新。如果對(duì)于同一個(gè)控件,連續(xù)多次刷新,可以添加一個(gè)成員變量作為標(biāo)記,以免同一控件連續(xù)多次刷新,提升部分性能。
補(bǔ)充:在主線程調(diào)用耗時(shí)操作用此方法可能會(huì)有問題,經(jīng)過(guò)驗(yàn)證調(diào)用Invoke函數(shù),其實(shí)是在主線程刷新界面。
如對(duì)本文有疑問,請(qǐng)?zhí)峤坏浇涣髡搲瑥V大熱心網(wǎng)友會(huì)為你解答??! 點(diǎn)擊進(jìn)入論壇