WM_PAINT是Windows視窗系統中一條重要的訊息,
應用程式通過處理該訊息實現在視窗上的繪製工作。
1. 系統何時發送WM_PAINT訊息?
系統會在多個不同的時機發送WM_PAINT訊息:當第一次建立一個視窗時,當改變視窗的大小時,當把視窗從另一個視窗背後移出時,當最大化或已最小化的視窗時,等等,這些動作都是由系統管理的,應用只是被動地接收該訊息,在訊息處理函數中進行繪製操作;大多數的時候應用也需要能夠主動引發視窗中的繪製操作,比如當視窗顯示的資料改變的時候,這一般是通過InvalidateRect和
InvalidateRgn函數來完成的。
InvalidateRect和InvalidateRgn把指定的地區加到視窗的Update Region中,當應用的訊息佇列沒有其他訊息時,如果視窗的Update Region不為空白時,系統就會自動產生WM_PAINT訊息。
系統為什麼不在調用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是否為空白等。
2. BeginPaint
BeginPaint和WM_PAINT訊息緊密相關。試一試在WM_PAINT處理函數中不寫BeginPaint會怎樣?程式會像進入了一個死迴圈一樣達到驚人的CPU佔用率,你會發現程式總在處理一個接一個的WM_PAINT訊息。這是因為在通常情況下,當應用收到WM_PAINT訊息時,視窗的Update Region都是非空的(如果為空白就不需要發送WM_PAINT訊息了),BeginPaint的一個作用就是把該Update
Region置為空白,這樣如果不調用BeginPaint,視窗的Update Region就一直不為空白,如前所述,系統就會一直發送WM_PAINT訊息。
BeginPaint和WM_ERASEBKGND訊息也有關係。當視窗的Update Region被標誌為需要擦除背景時,BeginPaint會發送WM_ERASEBKGND訊息來重畫背景,同時在其返回資訊裡有一個標誌表明視窗背景是否被重畫過。
當我們用InvalidateRect和InvalidateRgn來把指定地區加到Update Region中時,可以設定該地區是否需要被擦除背景,這樣下一個BeginPaint就知道是否需要發送WM_ERASEBKGND訊息了。
另外要注意的一點是,BeginPaint只能在WM_PAINT處理函數中使用。
補充幾點:
1.WM_Paint 是一個被動訊息,不能通過普通的方法簡單的 sendmessage WM_paint 了事
這是不行的;但通過訊息由程式員引發不是不可能;通過幾個特殊的常數可以做到,不過要到delphi下找
2.sendmessage 可以將訊息發送到訊息佇列;但windows會自動判斷是否存在無效的畫圖地區;
如果存在無效的畫圖地區,則可能會重畫,反之則棄用該訊息.
3.可以使用 InvalidateRect 等幾個APi將螢幕上任意一個個矩形地區設定為無效地區,在UpdateWindow後調用後,windows會自動尋找是否存在無效,並重畫,該矩形區