適用場景:
需要在某容器控件中動態裝載多個子控件,而且該容器控件可能需要改變WindowFormState,即從Normal轉變為Maxmized,或者是其他狀態轉換啦, what ever :)
如果沒有應用任何特殊處理,你就會發現,當容器控件狀態轉換時,其上的子控件在經過一陣狂閃之後(可能背景控件顏色和自身相互交替出現),最終恢復至平靜; 這種情形當然無論是程序員自己和客戶都不願意看到的
ok,廢話一通之後,咱們開始解釋原因,以及考慮解決方案
為什麼會閃爍?
因為窗體控件狀態轉換時,Windows需要負責"擦除"其背景,重新繪制,在一台性能並不優良的終端上(很大可能程度上客戶端電腦都不是那麼強勁吧) ,這個過程不是一時半會就能完成的,尤其對於很多個子控件的情況,因此就…
解決之道?
如果稍微寫過WinForm程序的同學,肯定或多或少的用過ListVIEw控件,簡單易用嘛 :) 那麼一定也知道該控件有2個比較有意思的方法:
BeginUpdate
Prevents the control from drawing until the EndUpdate method is called.
EndUpdate
Resumes drawing of the list vIEw control after drawing is suspended by the BeginUpdate method.
從msdn的解釋來看,這2個方法的應用能解決往ListView控件中分多次Add ListVIEwItem時閃爍的問題,ok,既然它能這麼處理,咱們自己的容器控件為什麼不能依葫蘆畫瓢呢?
btw. 其實我一開始也沒任何好方法解決閃爍問題,後來偶爾想到ListVIEw的此特性 :)
看看ListVIEw.BeginUpdateInternal方法怎麼寫:
internal void BeginUpdateInternal()
{
if (this.IsHandleCreated)
{
if (this.updateCount == 0)
{
this.SendMessage(11, 0, 0);
}
this.updateCount = (short) (this.updateCount + 1);
}
}
關鍵一行在 this.SendMessage(11, 0, 0); 蝦米意思呢? 它給自身Send了一個code為11的Windows消息,11代表蝦米?
在windows消息定義中可以看到 WM_SETREDRAW = 0x0B (0x0B也就是11),這行代碼的意思是告訴Windows對ListVIEw控件停止重繪界面,直到顯式要求重新繪制為止. 很牛叉對不對 :Dok,在EndUpdateInternal中又做了蝦米?
internal bool EndUpdateInternal(bool invalidate)
{
if (this.updateCount <= 0)
{
return false;
}
this.updateCount = (short) (this.updateCount - 1);
if (this.updateCount == 0)
{
this.SendMessage(11, -1, 0);
if (invalidate)
{
this.Invalidate();
}
}
return true;
}
同樣有一行代碼: this.SendMessage(11, –1, 0); 11還是同一個意思,此時告知Windows可以重繪ListVIEw控件了
ok,到這時候應該明白這2個方法含義了吧,也就是說對子控件的操作都是在一個“凍結”的狀態中進行的,等到所有准備工作就緒,才對最終狀態重新繪制,因此界面就不會出現閃爍狀態.