window窗口閃爍小結

首先,這兩天的論壇上,很多人又提“防閃爍”的問題;然後是讀了這位淺墨老兄的這篇博客,有感而發。

(這位老兄貌似也是轉發的,淺墨兄文筆很好,超讚)http://blog.csdn.net/zhmxy555/article/details/7436397


閃爍的問題有時候很難,涉及的東西太多;有時候很簡單,處理WM_ERASEBKGND消息即可;有時候需要雙緩衝;有時候還要改窗口樣式。。。

其實要真正處理好,需要知道原理、原因兩個“原”就可以對症下藥了。如果覺得簡單,就跳過好了,但很可能是你遇到的情況不夠複雜。

(以下以普通的windows窗口爲例)


第一,原理。

從人的視覺角度來講,如果兩張圖片的視覺效果反差極大,當這兩張圖片切換的時候,人眼會有刺眼和難受的感覺,產生閃爍。

所以說,你給用戶展示的東西本身有極大反差的時候,是用任何方法都無法消除閃爍的。

再看普通的windows窗口,它的繪製過程是先畫白色或淡灰色背景(View或Dialog,默認處理WM_ERASEBKGND),

然後再畫前景(圖像或子窗口和控件繪製,Draw/Paint)。圖像或子控件的位置由背景色變成前景色,人眼會看到這個過程,從而產生閃爍。

所以,普通情況下,單單用雙緩衝是無法避免閃爍的


第二,原因。原因肯定是在閃爍的位置,有視覺上的切換反差,消除即可。那麼我們必須要做到的,就是要找對地方,就是哪個地方導致了閃爍。

有的窗口很簡單,就兩層,窗口加子控件;有的就比較複雜了(比如我遇到的,視圖View,上面嵌一層TabView,再上面分左右兩個View,

左右兩個View上又佈滿子控件(甚至大於255個),控件還可能要切換(隱藏、顯示、摺疊),還包含GroupBox(這個提出來,是因爲確實比較特殊))。

這種情況包含了4層窗口,還要動態變化。

無論多少層的,都一個辦法,從下到上,一層一層的解決。


第三,找對地方之後,我們就要下猛藥了。(確實因爲情況比較多,這裏就舉一些常見的情況,且情況和解決方法不是一對一的。)

1.完全被其他窗口覆蓋的底層窗口,這種情況最簡單了,可以處理WM_ERASEBKGND消息,直接返回TRUE(反正都會被覆蓋掉);

如果上面有子窗口覆蓋,可以增加窗口樣式WS_CLIPCHILDREN(視情況加WS_CLIPSIBLINGS,以下再提到則省略)。

2.部分被其他窗口覆蓋的父窗口,比如FormView,可以增加窗口樣式WS_CLIPCHILDREN。

3.由於一系列MoveWindow子窗口引起的閃爍,可以用BeginDeferWindowPos, DeferWindowPos,EndDeferWindowPos系列函數。

4.如果是繪圖窗口,有很多圖形元素(比如CAD等,可能一屏顯示成千上萬圖元),就需要用內存DC(也就是雙緩存)。因爲繪製週期要很長時間,

中間會輸出到屏幕上,造成圖像一點一點出來的動畫的感覺)。當然,此時的優化也是必須的(不出現在屏幕上的圖元不繪製)。

5.對於有背景圖或需要重繪背景圖的情況,要處理WM_ERASEBKGND+雙緩存。

6.對於內嵌的IE窗口,我使用處理WM_ERASEBKGND的方法(::SetWindowLong(..., GWL_WNDPROC...),不過要找3層窗口),淺墨兄文中提到的還未嘗試用過。

7.對於隱藏、顯示、摺疊的窗口,不多的話基本不用特殊處理,但是有時候會有副作用(花屏和錯位,下面會提到)

8.繪製部分:帶參數的InvalidRect,只繪製髒數據區(對於繪圖程序可能比較常用)

9.SetRedraw/RedrawWindow/LockWindowUpdate至於你用不用,反正我不常用,所以瞭解不多。

另外,深入要了解LockWindowUpdate可以移步這裏http://blog.csdn.net/BalonFan/article/details/1594475

要注意的是,還有很多情況沒有列舉出來,而且很多方法要綜合運用。


第四,副作用。

1.花屏,該畫的背景沒畫。最常見如下情況:窗口裏面有GroupBox(上面提到過),GroupBox裏有控件。當窗口樣式包含WS_CLIPCHILDREN的時候,

繪製背景的時候會把GroupBox剪切的,系統會指望子控件自己繪背景;而恰恰GroupBox是個極特殊的控件,其繪製的時候,只畫邊框部分,中心區透明,讓父窗口畫。

這樣你推我我推你,沒人畫,就花屏了。解決方法是SubClass GroupBox控件繪製背景,而有背景圖的時候,需要掌握好尺寸;或者不用控件,在GroupBox位置繪製背景;或者讓父窗口畫(父窗口窗口樣式不包含WS_CLIPCHILDREN,繪製背景的時候,把其他子控件剪切)。

2.錯位,一般用在滾動的視圖裏(比如模擬摺疊效果),都是座標沒轉換好,細心點就沒問題,注意ScrollSize,避免最下面空間不夠或大片空白。

3.靜態標籤控件淺灰色底紋,如果不想SubClass,直接處理WM_CTRLCOLOR,大部分地球人都知道。


錯誤之處,歡迎各位網友給予指正。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章