emWIN里面的無效重繪和windows很類似。
WM_InvalidateArea()和WM_InvalidateRect()只重繪指定的區域,其他區域不會重繪,這樣避免了閃爍,重繪發生在下次WM_PAINT消息中。
WM_InvalidateWindow()重繪整個窗口,可以看到明顯的閃爍。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
InvalidateRect函數中的參數TRUE表示系統會在你畫之前用背景色將所選區域覆蓋一次,默認背景色為白色,可以通過設置BRUSH來改變背景色。
Invalidate()之后:
...OnPaint()->OnPrepareDC()->OnDraw()
所以只是刷新在OnPaint()和OnDraw()函數中的繪圖語句。其它地方沒有影響。
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)
UpdateData()順便說下,這個函數不是刷新界面用的。
UpdateData();參數為FALSE時,將界面上控件綁定的變量的數據導到控件內,參數為TRUE時,導入方向則相反
系統會在多個不同的時機發送WM_PAINT消息:當第一次創建一個窗口時,當改變窗口的大小時,當把窗口從另一個窗口背后移出時,當最大化或最小化窗口時,等等,這些動作都是由系統管理的,應用只是被動地接收該消息,在消息處理函數中進行繪制操作;大多數的時候應用也需要能夠主動引發窗口中的繪制操作,比如當窗口顯示的數據改變的時候,這一般是通過InvalidateRect和 InvalidateRgn函數來完成的。InvalidateRect和InvalidateRgn把指定的區域加到窗口的Update Region中,當應用的消息隊列沒有其他消息時,如果窗口的Update Region不為空時,系統就會自動產生WM_PAINT消息。 系統為什么不在調用Invalidate時發送WM_PAINT消息呢?又為什么非要等應用消息隊列為空時才發送WM_PAINT消息呢?這是因為系統把在窗口中的繪制操作當作一種低優先級的操作,於是盡可能地推后做,這樣有利於提高繪制的效率:在兩個WM_PAINT消息之間多個Invalidate調用使之失效的區域就會被累加起來,然后在一個WM_PAINT消息中一次得到更新,不僅能避免多次重復地更新同一區域,也優化了應用的更新操作。像這種通過InvalidateRect和InvalidateRgn來使窗口區域無效,依賴於系統在合適的時機發送WM_PAINT消息的機制實際上是一種異步工作方式,也就是說,在無效化窗口區域和發送WM_PAINT消息之間是有延遲的;有時候這種延遲並不是我們希望的,這時我們當然可以在無效化窗口區域后利用SendMessage 發送一條WM_PAINT消息來強制立即重畫,但不如使用Windows GDI為我們提供的更方便和強大的函數:UpdateWindow和RedrawWindow。UpdateWindow會檢查窗口的Update Region,當其不為空時才發送WM_PAINT消息;RedrawWindow則給我們更多的控制:是否重畫非客戶區和背景,是否總是發送WM_PAINT消息而不管Update Region是否為空等 |
csdn:我調試程序單步執行,在OnPaint()進入處設置了斷點,在程序首次進入OnPaint()前,有幾處Invalidate()調用,我發現Invalidate()執行后就繼續執行下面的語句了,並沒有進入OnPaint(),為什么呀? 還有就是OnPaint()的首次執行是由什么引發的?
OnPaint()消息的優先級比較低啊
要想立即執行OnPaint(),可以調用API函數UpdateWindow(hwnd);//hwnd為窗口句柄.
2.一個窗口的多個WM_PAINT,在一次消息循環中統一處理,就是說只調用一次OnPaint.
3.立即刷新可以調用UpdateWindow,WM_PAINT也會在調用該函數后從消息隊列中除去.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Invalidate與UpdateWindow
Invalidate()函數
Msdn:
Invalidates the entire client area of CWnd. The client area is marked for painting when the nextWM_PAINTmessage occurs. The region can also be validated before aWM_PAINT message occurs by theValidateRect orValidateRgn member function. (使客戶區域無效。)
Msdn:
Updates the client area by sending a WM_PAINT message if the update region is not empty. TheUpdateWindow member function sends aWM_PAINT message directly, bypassing the application queue. If the update region is empty,WM_PAINT is not sent.(如果更新區域不為空,則通過發送WM_PAINT消息來更新客戶區。UpdateWindow函數直接發送WM_PAINT消息,不經過應用程序的消息隊列。如果更新區域為空,則不發送WM_PAINT消息)
從msdn的解釋可以看出,Invalidate只是標志無效區域,而不是立即更新客戶區域,若要想立即更新,則要在Invalidate后執行UpdateWindow語句,該函數通過發送WM_PAINT消息可以立即更新客戶區域(更新區域不為空的情況下)。
而通過在debug下跟蹤TRACE語句輸出也驗證了上面的觀點,若只調用Invalidate函數,則必須在程序的所有語句執行完畢后OnPaint函數才會響應。相反,若在執行Invalidate函數后立即執行UpdateWindow函數,則程序會在執行UpdateWindow函數后立即發送WM_PAINT消息,OnPaint函數去響應,之后再去執行UpdateWindow后面的語句。
下面這兩段代碼非常有意思,它們是按鈕的響應函數:
- void CASSSDlg::OnBnClickedButton1()
- {
- Invalidate(true); //使窗口的客戶區無效
- //UpdateWindow();
- CString sTemp;
- sTemp="能看見我么?";
- CClientDC dc(this);
- dc.TextOutW(0,0,sTemp);
- }
- //第二段:
- void CASSSDlg::OnBnClickedButton1()
- {
- Invalidate(true); //使窗口的客戶區無效
- UpdateWindow(); //發送WM_PAINT消息,立即更新窗口
- CString sTemp;
- sTemp="能看見我么?";
- CClientDC dc(this);
- dc.TextOutW(0,0,sTemp);
- }
這兩段代碼的執行效果就在於第二段代碼的客戶區輸出了sTemp語句,而第一段代碼卻沒有輸出。原因就在於第一段代碼沒有立即刷新客戶區,而是等到所有語句結束之后才發送WM_PAINT消息,也就是它在TextOut之后才刷新的,TextOut輸出的文字給刷掉了。其實不光是TextOut,畫出的圖也會被刷掉。
當然了,如果第一段代碼的Invalidate函數參數為false,則在客戶區也能看見TextOutW的輸出,這個原因雖然它也是等到所有語句結束之后才發送WM_PAINT消息,但由於Invalidate函數的參數若為false,則不擦除背景(TextOut的輸出做為背景被保留下來了),若為true,則用默認畫刷擦除背景,即TextOut輸出的文字給背景刷掉了。
原文:http://blog.csdn.net/liuy_yy/article/details/7190331