VC里OnPaint幾點要注意的地方(沒有invalidate,系統認為窗口沒有更新的必要,於是就對發來的WM_PAINT消息不理不睬)


寫在屬於自己的體會,哪怕只是一點點,也是真的懂了。否則有那么多書,如果只是不過腦子的學一遍看一遍,又有誰真的掌握了這些知識呢?

 

這樣你或許就明白了為什么不能直接用SendMessage和PostMessage發送WM_PAINT的原因:由於沒有invalidate,系統認為窗口沒有更新的必要,於是就對發來的WM_PAINT消息不理不睬。解決方案就是——我們自己invalidate!相關的API就是InvalidateRect()和InvalidateRgn(). 

 

還想說一下Invalidate和UpdateWindow的區別。Invalidate在消息隊列中加入一條WM_PAINT消息,其無效區為整個客戶區。而UpdateWindow直接發送一個WM_PAINT消息,其無效區范圍就是消息隊列中WM_PAINT消息(最多只有一條)的無效區(估計把消息從消息隊列中提出,直接發送給窗口,走關系戶的路子)。效果很明顯,調用Invalidate之后,屏幕不一定馬上更新,因為WM_PAINT消息不一定在隊列頭部,而調用UpdateWindow會使WM_PAINT消息馬上執行的,繞過了消息隊列。如果你調用Invalidate之后想馬上更新屏幕,那就加上UpdateWindow()這條語句

 

用WM_PAINT處理重畫是異步(asynchronous)的。也就是說,在invalidate之后窗口並不會立即重畫而是等到消息隊列為空時再重畫,這樣就有一個時間差。這個事件差有時短到不被注意,但有時就是個大問題(尤其是當程序需要執行耗費時間的任務,如串口I/O)。這時可以采用同步重畫法,直接用GetDC()獲得hDC執行重畫操作。如果非要使用WM_PAINT來同步重畫(個人比較喜歡這種方法,和重畫有關的代碼就應該在WM_PAINT的處理程序里嘛),可以使用UpdateWindow()和RedrawWindow(). 這兩個API函數會直接把WM_PAINT送進窗口的消息隊列而不是應用程序的消息隊列,這樣就不用等到最后了。注意前者當update region不為空時才會發送WM_PAINT,后者的控制選項更為豐富。

 

RedrawWindow相當於先調用InvalidateRect,緊接着又調用UpdateWindow,此外RedrawWindow還提供了一些前兩者沒法做到的功能。

 

這個WM_PAINT消息既可能由系統發送,也可能由應用程序人工發送(比如自繪時需要)。當鼠標指向這個區域時加載hover圖像以獲得hottrack效果。這時操作系統自然不會認為有重畫的必要,但程序卻必須重畫,這時就得人工發送WM_PAINT消息了。注意不要傻乎乎地直接用SendMessage或PostMessage發送WM_PAINT,后面會解釋原因。重畫很費時間和資源,並且也不是應用程序的“主業”,因此系統也知道要盡量減少重畫的次數。系統只在應用程序的消息隊列為空的時候才發送WM_PAINT,這就是為什么當程序死鎖時窗口圖像不會更新。同樣為了減少重畫的工作量,Windows提出了update region的概念。

 

比如原來在窗口上面的一個窗口現在挪走了,系統就把新露出來的區域定義為update region(這個過程稱為invalidate)。系統不斷檢測一個窗口的update region是否為空,當update region不為空並且應用程序沒有消息要處理(消息隊列為空)的時候,系統就通過WM_PAINT告訴應用程序“現在沒事干了?窗口的一部分需要重畫,你把這一部分重畫一下”。應用程序重畫了窗口之后,把update region重新設置為空(這個過程稱為validate),如此不斷循環。如果消息隊列不為空,系統就把update region不斷更新(采用取並集的方法),等消息隊列為空的時候一起處理。這就大大減少了重畫的次數。

 

參考:

http://hi.baidu.com/pro_lily/item/a5c38afffac2495ac9f33721
http://hi.baidu.com/dongyiju2/item/3f8c2a10725e2526f7625c36

 

--------------------------- WM_PAINT 的產生原因 ------------------------------

當WM_PAINT不是由InvalidateRect產生時,即由最大化,最小化等產生時,或者移動產生(移動有時只會產生WM_ERASEBKGND消息)系統先發送WM_ERASEBKGND消息,再發送WM_PAINT消息.
如果處理WM_ERASEBKGND消息時返回FALSE,BeginPaint標記pt.fErase 為TRUE,如果處理WM_ERASEBKGND時返回TRUE,BeginPaint標記pt.fErase為FALSE.

當WM_PAINT由InvalidateRect產生時,先發送WM_PAINT消息(讀書筆記:再看情況發送WM_ERASEBKGND)(異步),如果InvalidateRect的bErase為TRUE,BeginPaint檢查到更新區域需要刪除背景,向窗口發送一個WM_ERASEBKGND消息,如果處理WM_ERASEBKGND消息時返回FALSE,BeginPaint標記pt.fErase 為TRUE,如果處理WM_ERASEBKGND時返回TRUE,BeginPaint標記pt.fErase為FALSE.
如果pt.fErase標記為TRUE,指示應用程序應該處理背景,但是應用程序不一定需要處理,pt.fErase只是作為一個標記.

http://hi.baidu.com/dirtyface001/item/d6765b0d8a47338f03ce1b28
http://hi.baidu.com/qinfengxiaoyue/item/1a8f260ccbd3c528a0312d08


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM