窗口繪制
WM_PAINT 消息:
Windows通過發送 WM_PAINT 消息來通知窗口過程其客戶區需要重繪。
大多數 Windows 程序在 WinMain 函數初始化過程中會在進入消息循環之前調用 UpdateWindow() 函數。
這將會向窗口過程函數( WndProc() )發送最初的 WM_PAINT消息,該消息通知窗口過程繪制客戶區。
在以下任何事件發生時,窗口過程都會收到一條 WM_PAINT 消息;
- 用戶移動一個窗口,導致原來被遮擋的部分窗口暴露出來。
- 用戶調整窗口大小。
- 程序調用 ScrollWindow() 或 ScrollDC() 函數滾動客戶區。
- 程序調用 InvalidateRect() 或 InvalidateRgn() 函數顯示生成 WM_PAINT 消息。
在以下情形,Windows 有時 會發生一條 WM_PAINT 消息。
- Windows 關閉一個覆蓋了部分窗口過程的對話框或消息框。
- 下拉菜單被拉下然後收回。
- 顯示提示信息。
這些情況如下:
- 鼠標指針在客戶區內移動。
- 在客戶區內拖到圖標。
有效矩形和無效矩形:
盡管窗口過程必須能夠在收到 WM_PAINT 消息時更新整個客戶區,但通常它只需要更新其中的一部分。
最明顯的例子是當一個對話框覆蓋了客服區的一部分時:當對話框關閉時,只有先前被覆蓋的那個矩形部分需要重新繪制。
需要重新繪制的部分被稱為“無效區域”或“更新區域”。
在客戶區中有一個無效區域將導致 Windows 在應用程序的消息隊列中放置一條 WM_PAINT 消息。
只有當程序客戶區的一部分失效時,窗口過程才會接受到 WM_PAINT 消息。
Windows 內部為每一個窗口都保存了一個“繪制信息結構”(PAINTSTRUCT)。
該結構保存著一個可以覆蓋該無效區域的最小矩形的坐標和一些其他的信息。這個最小區域稱為“無效矩形”。
如果在窗口過程處理一條等候處理 WM_PAINT 消息之前,客戶區的另外一部分也失效了,
那麽 Windows 將計算出一個覆蓋這兩個失效部分的新的無效區域和無效矩形,並更新繪制信息結構中的數據。
也就是說將仍只有一條 WM_PAINT 消息。
窗口過程可以通過 InvalidateRect() 函數來強制使自己的客戶區的一個矩形失效,也可以調用 ValidateRect() 函數來強制使客戶區變的有效。
如果是讓整個無效區域都有效,那麽消息隊列中的 WM_PAINT 消息就會被刪除。這兩個函數在有時候是很有用處的。
窗口過程在處理 WM_PAINT 消息時,在調用BeginPaint() 函數時,整個客戶區會變有效的 。
設備環境:
當程序完成對客戶區的繪制後,它必須釋放設備環境句柄。釋放後該句柄將不再有用。
程序必須在處理同一條消息的過程中獲取句柄和釋放句柄。
你不能在兩條消息中間傳遞一個設備環境句柄,唯一的例外是通過調用 CreateDC() 函數創建設備環境。
獲取設備環境句柄:方法一
這種方法可以在處理 WM_PAINT 消息時使用。這關系到兩個函數:BeginPaint() 和 EndPaint() 。
這兩個函數都需要兩個參數:一個是窗口的句柄,這是消息處理過程的參數:另一個是一個類型為 PAINTSTRUCT 結構的變量的地址。
程序通常將該結構體變量命名為 ps 。
在處理 WM_PAINT 消息時,窗口過程首先調用BeginPaint() 函數,該函數通常會擦去無效區域的背景以便繪圖。
它同時還會填充 ps 結構的各個字段。函數的返回值就是設備環境句柄。通常會將它保存在一個名為 hdc 的變量中。
窗口過程在處理 WM_PAINT 消息時必須成對的調用 BeginPaint() 和 EndPaint() 。
如果窗口過程不處理 WM_PAINT 消息,該消息就會被傳送給 Windows 默認的窗口過程 DefWindowProc 。
獲取設備環境句柄:方法二
盡管最後在程序處理 WM_PAINT 消息時才更新整個客戶區,
但有時也會發現在處理非 WM_PAINT 消息時繪制部分客戶區也是很有用的。
這時你可以調用 GetDC() 函數來獲得窗口客戶區的設備環境句柄,使用完成後也必須調用 ReleaseDC() 函數將它釋放:
hdc = GetDC(hwnd);
releaseDC(hwnd,hdc);
這個函返回的句柄和 BeginPaint() 函數返回的有所不同,從 GetDC() 返回的設備環境句柄中的裁剪矩形是整個客戶區。
這意味著你可以在客戶區的任意部分繪制,而不局限於無效矩形。另外,GetDC() 函數將不會使無效區域有效化。
如果想使整個客戶區有效,可以調用 ValidateRect() 函數。
TEXTOUT() 是顯示文本的最重要的 GDI 函數,它的語法如下:
TEXTOUT(hdc, x, y, psText, iLength);
第一個參數是設備環境句柄:它既可以是從 GetDC() 返回的 hdc 值,也可以是處理 WM_PAINT 消息時
從 BeginPaint() 返回的 hdc 值。
設備環境中的屬性決定了文本顯示的特性。例如,設備環境中的一個屬性決定了文本的顏色,默認顏色為黑色。
設備環境同時默認文本的背景顏色為白色。當程序輸出文本時,Windows 將用這個背景色填充每個字符周圍的矩形區域 (我們稱之為"字符框")。
文本背景色和在窗口類中設定的背景色是不一樣的。窗口類中的背景色 Windows 用來擦除客戶區的刷子,
它是一種圖樣,既可以是純色也可以不是純色的。它並不是設備環境結構的一部分。
在定義窗口類時,大多數應用程序選擇WHITE_BRUSH 作為背景,這樣設備環境中默認的文本背景色 就和 Windows 用來擦除客戶區的背景色相同了。
參數 psText 是指向字符串的指針,而 iLength 是字符串中的字符數。如果 psText 指向一個 Unicode 字符串,
則字符串占用的字節數是將是 iLength 的兩倍。字符串中不應該有任何 ASCII 控制字符,如 回車鍵,換行符,制表符或退格鍵
Windows 將這些控制字符顯示為空心或實心方塊。 Textout() 函數並不認為字符串尾部值為0字節
(對Unicode 而言是值為0的雙字節)表示字符串結束,它利用 iLength 參數來決定字符串的長度。
參數 x 和 y 決定著輸出字符串的在客戶區的起始位置。 x 是水平位置,而 y 是垂直位置。
第一個字符的左上角定位在坐標點 (x,y)。在默認的設備環境中,坐標原點位於客戶區的左上角。
如果傳給 TEXTOUT() 函數的 x 和 y 都是 0,則字符串將緊靠客戶區左上角。
在類似於 TEXTOUT() 的 GDI 繪圖函數的文檔中,傳給函數的坐標值通常被稱為"邏輯坐標"。
現在要註意的是,Windows 有各種"映射模式",來決定怎樣將 GDI 函數中的邏輯坐標
轉換為顯示器上的物理像素坐標。這些映射模式在設備環境中定義。
默認的映射模式是 MM_TEXT(標識符定義在 WINGDI.H 頭文件中)。
在 WM_TEXT 映射模式下,邏輯單位和物理單位都是像素點。
坐標是相對於客戶區的左上角, x 的值從左往右增大,y 的值從上往下增大。
窗口繪制