MFC重繪原理的關鍵理解


// ====================Windows重繪消息與函數==========================

得到桌面窗口的句柄,然后再繪圖
HWND GetDesktopWindow(VOID);

當需要更新或重新繪制窗口的外觀時,應用程序就會發送WM_PAINT消息對窗口進行重新繪制。


Invalidate()是強制系統進行重畫,但是不一定就馬上進行重畫。因為Invalidate()只是通知系統,此 時的窗口已經變為無效。強制系統調用WM_PAINT,而這個消息只是Post就是將該消息放入消息隊列。當執行到WM_PAINT消息時才會對敞口進行重繪。

UpdateWindow只向窗體發送WM_PAINT消息,在發送之前判斷GetUpdateRect(hWnd,NULL,TRUE)看有無可繪制的客戶區域,如果沒有,則不發送WM_PAINT。

RedrawWindow()則是具有Invalidate()和UpdateWindow()的雙特性。聲明窗口的狀態為無效,並立即更新窗口,立即調用WM_PAINT消息處理。

調用Invalidate之后,屏幕不一定馬上更新,因為WM_PAINT消息不一定在隊列頭部,而調用UpdateWindow會使WM_PAINT消息馬上執行的,繞過了消息隊列。如果你調用Invalidate之后想馬上更新屏幕,那就加上UpdateWindow()這條語句。

為什么不直接使用虛擬函數實現消息處理函數呢?這是一個GOOD QUESTION。前面已經說過,MFC的設計者們在設計MFC時有一個很明確的目標,就是使得“MFC的代碼盡可能小,速度盡可能快”,如果采用虛擬函數,那么對於所有的窗口消息,都必須有一個與之對應的虛擬函數,因而對每一個從CWnd派生的類而言,都會有一張很大的虛擬函數表vtbl。但是在實際應用中,一般只對少數的消息進行處理,大部分都交給系統缺省處理,所以表中的大部分項都是無用項,這樣做就浪費了很多內存資源這同MFC設計者們的設計目標是相違背的。當然,MFC所使用的方法只是解決這類問題的方式之一,不排除還有其他的解決方式,但就我個人觀點而言,這是一種最好的解決方式,體現了很高的技巧性,值得我們學習。

  至於這第二個問題,是由上面的問題引申出來的。如果在子類和父類中出現了相同的消息出來函數,VC編譯器會怎么處理這個問題呢?VC不會將它們看作錯誤,而會象對待虛擬函數類似的方式去處理,但對於消息處理函數(帶afx_msg前綴),則不會生成虛擬函數表vtbl

 

Invalidate過程看成類似CombineRgn()取並集,把validate過程看成取差集即可。

 

用WM_PAINT處理重畫是異步(asynchronous)的。也就是說,在invalidate之后窗口並不會立即重畫而是等到消息隊列為空時再重畫,這樣就有一個時間差。這個事件差有時短到不被注意,但有時就是個大問題(尤其是當程序需要執行耗費時間的任務,如串口I/O)。

這時可以采用同步重畫法,直接用GetDC()獲得hDC執行重畫操作。如果非要使用WM_PAINT來同步重畫(個人比較喜歡這種方法,和重畫有關的代碼就應該在WM_PAINT的處理程序里嘛),可以使用UpdateWindow()和RedrawWindow(). 這兩個API函數會直接把WM_PAINT送進窗口的消息隊列而不是應用程序的消息隊列,這樣就不用等到最后了。注意前者當update region不為空時才會發送WM_PAINT,后者的控制選項更為豐富。

 

OnEraseBkGnd(),是窗口背景需要刷新時由系統調用的。明顯的一個例子是設置窗口的背景顏色(你可以把這放在OnPaint中去做,但是會使產生閃爍的現象)。
OnPaint()用來響應WM_PAINT消息,視類的OnPaint()內部根據是打印還是屏幕繪制分別以不同的參數調用OnDraw()虛函數。所以在OnDraw()里你可以區別對待打印和屏幕繪制。
其實,MFC在進行打印前后還做了很多工作,調用了很多虛函數,比如OnPreparePrint()等。
OnCtlColor()是當窗口的控件需要繪制時發生的,它將繪制窗口的控件。

GetUpdateRect(GetSafeHwnd(), &R, B);

 

參考:

http://www.cnblogs.com/sfqh/p/3384730.html

------------------------------------------------------------------------------

Invalidate函數的總結
InvalidateRect只是增加重繪區域,下次WM_PAINT的時候才生效
InvalidateRect函數中的參數TRUE表示系統會在你畫之前用背景色將所選區域覆蓋一次,默認背景色為白色,可以通過設置BRUSH來改變背景色。
Invalidate標記一個需要重繪的無效區域,並不意味着調用該函數后就立刻進行重繪。類似於PostMessage(WM_PAINT),需要處理到WM_PAINT消息時才真正重繪。以為您Invalidate之后還有其他的語句正在執行,程序沒有機會去處理WM_PAINT消息,但當函數執行完畢后,消息處理才得以進行。

Invalidate只是放一個WM_PAINT消息在隊列里,不做別的,所以只有當當前函數返回后,進入消息循環,取出WM_PAINT,才執行PAINT,所以不管Invalidate放哪里,都是最后的。
InvalidateRect(hWnd,&rect,TRUE);向hWnd窗體發出WM_PAINT的消息,強制客戶區域重繪制,rect是你指定要刷新的區域,此區域外的客戶區域不被重繪,這樣防止客戶區域的一個局部的改動,而導致整個客戶區域重繪而導致閃爍,如果最后的參數為TRUE,則還向窗體發送WM_ERASEBKGND消息,使背景重繪,當然在客戶區域重繪之前。
UpdateWindow只向窗體發送WM_PAINT消息,在發送之前判斷GetUpdateRect(hWnd,NULL,TRUE)看有無可繪制的客戶區域,如果沒有,則不發送WM_PAINT
如果希望立即刷新無效區域,可以在調用InvalidateRect之后調用UpdateWindow,如果客戶區的任一部分無效,則UpdateWindow將導致Windows用WM_PAINT消息調用窗口過程(如果整個客戶區有效,則不調用窗口過程)。這一WM_PAINT消息不進入消息隊列,直接由WINDOWS調用窗口過程。窗口過程完成刷新以后立刻退出,WINDOWS將控制返回給程序中UpdateWindow調用之后的語句。(windows程序設計第5版 P98)

Invalidate()之后:(MFC的,順便了)
OnPaint()->OnPrepareDC()->OnDraw()
所以只是刷新在OnPaint()和OnDraw()函數中的繪圖語句。其它地方沒有影響。

UpdateData()順便說下,這個函數不是刷新界面用的。
UpdateData();參數為FALSE時,將界面上控件綁定的變量的數據導到控件內,參數為TRUE時,導入方向則相反。

另外,Delphi里響應WM_ERASEBKGND使用FillRect重畫背景

參考:

http://www.360doc.com/content/10/0823/10/1066008_48105283.shtml


免責聲明!

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



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