c#winform控件过多卡顿问题解决方案
近期在項(xiàng)目中遇到了登錄以及切換界面時(shí),界面不停閃爍的問(wèn)題,經(jīng)過(guò)查閱發(fā)現(xiàn)以下方法可以成功解決該問(wèn)題。
將下面代碼添加到窗體代碼中即可:
protected override CreateParams CreateParams //防止界面閃爍
{
get
{
CreateParams paras = base.CreateParams;
paras.ExStyle |= 0x02000000;
return paras;
}
}
一、通過(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ù)里加上以下代碼:
1 this.DoubleBuffered = true;//設(shè)置本窗體 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();?
二、C#控件的閃爍問(wèn)題解決方法總結(jié)
最近對(duì)代碼作了一些優(yōu)化,試驗(yàn)后效果還可以,但是發(fā)現(xiàn)界面會(huì)閃爍,具體是TreeView控件會(huì)閃爍,語(yǔ)言為C#,IDE為VS2005。在查閱一些資料,使用了一些基本技術(shù)后(如開(kāi)啟雙緩沖),發(fā)現(xiàn)沒(méi)什么效果。
? ? ? ? 于是使用Profiler工具,查找出瓶頸在于每次更新完界面的EndUpdate操作(使用這個(gè)是為了減少界面更新次數(shù),但這里不理想是因?yàn)榭丶兄械脑睾芏?#xff09;,猜想大概每次更新,.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ù)操作,禁掉這條消息。代碼如下:
成功!
?
注:雙緩沖還是有用的,在更新不是很頻繁且控件內(nèi)含元素不是特別多的時(shí)候。一旦元素過(guò)多,每次更新時(shí)間都比較長(zhǎng),即便使用了雙緩沖,仍解決不了閃爍問(wèn)題。個(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)是跟前面正確解決方案一致,也是禁止清除背景并開(kāi)啟雙緩沖,但需要使用用戶繪制選項(xiàng),而且是全部交由用戶繪制。這需要自己實(shí)現(xiàn)控件的全部繪制,比較麻煩。所以這個(gè)方法不是完全不可行,但是需要額外工作量,不推薦。我也沒(méi)有使用。
2)使用BeginUpdate和EndUpdate
? ? ? 這一對(duì)操作對(duì)于需要批量操作更新控件的情景有比較好的效果,比如初始化時(shí)批量添加了大量節(jié)點(diǎn)。壞處就在于不能即時(shí)更新。所以,對(duì)于頻繁的更新節(jié)點(diǎn)并希望立即反映到界面的情況不適用。如果使用并且沒(méi)有禁掉清除界面消息的話,則控件看起來(lái)就會(huì)不停的閃爍,而且以白底為主,內(nèi)容幾乎不可見(jiàn)(這個(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ú)效果,不知是什么原因,沒(méi)有細(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)單,開(kāi)單獨(dú)的線程,處理數(shù)據(jù),將處理數(shù)據(jù)顯示到界面就好。但是我們經(jīng)常需要在主程序運(yùn)算一些內(nèi)容,否則可能會(huì)改動(dòng)比較大。因此,這里講講第二種方式。
同樣是使用多線程,但是c#在其他線程刷新有一點(diǎn)點(diǎn)問(wèn)題,即不能跨線程操作界面。這可以使用控件的Invoke方法解決:
這樣可以讓控件在其它線程刷新界面。
再加上開(kāi)新線程后的通用方法:
這樣就可以在任何時(shí)候,調(diào)用此方法對(duì)控件進(jìn)行刷新,而不將整個(gè)界面刷新。如果對(duì)于同一個(gè)控件,連續(xù)多次刷新,可以添加一個(gè)成員變量作為標(biāo)記,以免同一控件連續(xù)多次刷新,提升部分性能。
補(bǔ)充:在主線程調(diào)用耗時(shí)操作用此方法可能會(huì)有問(wèn)題,經(jīng)過(guò)驗(yàn)證調(diào)用Invoke函數(shù),其實(shí)是在主線程刷新界面。
總結(jié)
以上是生活随笔為你收集整理的c#winform控件过多卡顿问题解决方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 解决:No goals have bee
- 下一篇: int 和 Integer 的区别