形成的原因
顯示區域的顯示內容怎么會變得無效呢?在最初建立視窗的時候,整個顯示區域都是無效的,因為程式還沒有在視窗上畫什麼東西。第一條WM_PAINT訊息(通常發生在WinMain中調用UpdateWindow時)指示視窗訊息處理程式在顯示區域上畫一些東西。在用戶改變HELLOWIN視窗的大小後,顯示區域的顯示內容重新變得無效。wndclass結構的style欄位設定為標誌CS_HREDRAW和CS_VREDRAW,這樣的格式設定指示Windows,在視窗大小改變後,就把整個視窗顯示內容當成無效。然後,視窗訊息處理程式將收到一條WM_PAINT訊息。當用戶將最小化,然後再次將視窗恢復為以前的大小時,Windows將不會保存顯示區域的內容。在圖形環境下,視窗顯示區域涉及的數據量很大。因此,Windows令視窗無效,視窗訊息處理程式接收一條WM_PAINT訊息,並自動恢復其視窗的內容。在移動視窗以致其相互重疊時,Windows不保存一個視窗中被另一個視窗所遮蓋的內容。在這一部分不再被遮蓋之後,它就被標誌為無效。視窗訊息處理程式接收到一條WM_PAINT訊息,以更新視窗的內容。定義
當需要繪製一部分套用視窗的時候,這個訊息被Windows或者其他應用程式繪製調用。這個訊息一般在調用UpdateWindow函式或者當應用程式通過GetMessage函式或者PeekMessage函式來獲得一個WM_PAINT訊息時候被DispatchMessage函式時候被傳送。WM_PAINT hdc=(HDC) wParam;
處理方式
對WM_PAINT的處理幾乎總是從一個BeginPaint調用開始:hdc = BeginPaint (hwnd, &ps) ;而以一個EndPaint調用結束:EndPaint (hwnd, &ps) ;在這兩個調用中,第一個參數都是程式的視窗句柄,第二個參數是指向型態為paintstruct的結構指針。PAINTSTRUCT結構中包含一些視窗訊息處理程式,可以用來更新顯示區域的內容。我們將在下一章中討論該結構的各個欄位。現在我們只在BeginPaint和EndPaint函式中用到它。
在BeginPaint調用中,如果顯示區域的背景還未被刪除,則由Windows來刪除。它使用註冊視窗類別的WNDCLASS結構的hbrBackground欄位中指定的畫刷來刪除背景。一般, 這是一個白色備用畫刷。這意味著,Windows將通過把視窗背景設定為白色來刪除視窗背景。BeginPaint調用令整個顯示區域有效,並傳回一個“設備上下文句柄”。設備上下文是指實體輸出設備(如視頻顯示器)及其設備驅動程式。在視窗的顯示區域顯示文字和圖形需要設備上下文句柄。但是從BeginPaint傳回的設備上下文句柄不能在顯示區域之外繪圖,讀者可以試一試。EndPaint釋放設備上下文句柄,使之不再有效。
如果視窗訊息處理程式不處理WM_PAINT訊息(這是很少見的),它們必須被傳送給DefWindowProc。DefWindowProc只是依次調用BeginPaint和EndPaint,以使顯示區域有效。調用完BeginPaint之後,WndProc接著調用GetClientRect:
GetClientRect (hwnd, ▭) ;
第一個參數是程式視窗的句柄。第二個參數是一個指針,指向一個RECT型態的rectangle結構。該結構有四個LONG欄位,分別為left、top、right和bottom。GetClientRect將這四個欄位設定為視窗顯示區域的尺寸。left和top欄位通常設定為0,right和bottom欄位設定為顯示區域的寬度和高度(像素點數)。WndProc除了將該RECT結構指針作為DrawText的第四個參數傳遞外,不再對它做其它處理
註:
系統為什麼不在調用Invalidate時傳送WM_PAINT訊息呢?又為什麼非要等套用訊息佇列為空時才傳送WM_PAINT訊息呢?這是因為系統把在視窗中的繪製操作當作一種低優先權的操作,於是儘可能地推後做。不過這樣也有利於提高繪製的效率:兩個WM_PAINT訊息之間通過InvalidateRect和InvaliateRgn使之失效的區域就會被累加起來,然後在一個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是否為空等。BeginPaint和WM_PAINT訊息緊密相關。試一試在WM_PAINT處理函式中不寫BeginPaint會怎樣?程式會像進入了一個死循環一樣達到驚人的CPU占用率,你會發現程式總在處理一個接 一個的WM_PAINT訊息。這是因為在通常情況下,當套用收到WM_PAINT訊息時,視窗的Update Region都是非空的(如果為空就不需要傳送WM_PAINT訊息了),BeginPaint的一個作用就是把該Update Region置為空,這樣如果不調用BeginPaint,視窗的Update Region就一直不為空,如前所述,系統就會一直傳送WM_PAINT訊息。