我們今天來吹一下關于窗口重繪的事情,在開始吹牛之前,我們先用上一篇博文中說到的方法寫一個簡單的Win32應用程序。代碼如下:
~~~
#include <Windows.h>
//先聲明一下消息處理函數
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
// 入口點
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrvInstance,
LPSTR lpCommandLine,
int cmdShow)
{
WCHAR* cln = L"MyApp";
//設計窗口類
WNDCLASS wc = {};
wc.hInstance = hInstance;
wc.lpszClassName = cln;
wc.lpfnWndProc = MyWindowProc;
//注冊窗口類
RegisterClass(&wc);
// 創建窗口
HWND hMainwind = CreateWindow(
cln,
L"繪制窗口",
WS_OVERLAPPEDWINDOW,
20,
12,
450,
300,
NULL,
NULL,
hInstance,
NULL);
// 顯示窗口
if(hMainwind == NULL)
return 0;
ShowWindow(hMainwind,SW_NORMAL);
// 消息循環
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// 窗口消息處理程序
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
break;
case WM_DESTROY:
PostQuitMessage(0);//退出程序
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
}
~~~
這個程序是可以正常運行的,我們先來運行一下,看看有什么效果,當然沒什么效果,因為只是一個空白窗口。

窗口是正常出現了,但是,現在你試一下改變它的大小時,你人發現有問題了。

這時候我們看到,窗口中有一部分內容變成黑色了,也就是說它沒有被重新繪制。當我們的窗口被另一個窗口擋住,然后另一個窗口被移開,我們的程序窗口重新顯示時,會發生重繪;但我們改變窗口的大小后,窗口也會發生重繪;當我們把窗口隱藏(如最小化)后再顯示,它也會發生重繪。我們的窗口就像一堵墻,在運行期間會不斷地被重新粉刷。
要讓窗口自動重繪,有一種簡單的方法,就是在注冊窗口類的時候,設置一個背景色。
~~~
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
~~~
現在你運行的時候,改變窗口的大小,就看不到黑色區域了,因為有了背景色。而上面的代碼中,有朋友可能會問,COLOR_WINDOW + 1是什么東西,背景色為什么加1?
我們不妨看看COLOR_WINDOW 的定義。

其實這些都是數值來的,我們看到COLOR_WINDOW是5,那么 5 + 1 就是6吧,你看看6是誰?是不是COLOR_WINDOWFRAME的值?所以,你現在懂了嗎,它只是選擇COLOR_WINDOW作為參考點,因此,你可以推算到 COLOR_WINDOW - 2是哪個顏色值了。
當窗口需要重新粉刷時,我們的程序都會收到一條WM_PAINT消息,我們可以響應它來完成繪制。
~~~
// 窗口消息處理程序
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
DrawText(ps.hdc, L"朋友,你好。",wcslen(L"朋友,你好。"), &(ps.rcPaint), DT_CENTER);
EndPaint(hwnd, &ps);
}
return 0;
...................
~~~
我們也許在其他書上看到過,繪制圖形要先GetDC來獲得一個HDC,然后畫圖,畫完之后再ReleaseDC,為什么我們這里不需要這樣做?從代碼中我們看到,繪圖代碼放在BeginPaint和EndPaint之間,這是專門用在處理WM_PAINT消息用的,注意在其他地方不能這樣用。
我們用DrawText函數在窗口上繪制了一行文本,但是,在你運行程序后,如果你調整了窗口的大小,你會發現又有新問題出現了。

現在,我們還是來想一想,為啥會出現這種情況呢?
那是因為窗口不是把整個客戶區域完全重畫,而是判斷哪一部分應該重畫,就重畫,先前因為我們用的都同一種顏色的畫刷來填充背景,所以我們看不出問題所在,然現在我們在窗口上繪制了文本,問題就顯現出來了。由于每次重畫時的矩形范圍都是不同的,而且在重畫之前沒有清除前面的內容,使得新畫的東西覆蓋在原來的上面,就好像我們拿油漆在墻壁上涂鴉一樣。
窗口的客戶區域就是你看到的白色的那塊矩形,除了標題欄和邊框,剩下的那些都可以歸為客戶區域。
要解決上面問題的最簡單方法,就是在設計窗口類的時候,把類樣式同時設為水平重畫(CS_HREDRAW)和垂直重畫(CS_VREDRAW).
~~~
wc.style = CS_HREDRAW | CS_VREDRAW;
~~~
現在再運行一下,哈哈,沒問題了。

上面的文本是使用默認顏色的繪制的,我想改一下文本的顏色,可以使用SetTextColor函數,第一參數接受一個hdc,第二個參數是COLORREF,通過RGB宏可以創建。如下面的例子:
~~~
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
SetTextColor(ps.hdc, RGB(10, 0, 255));//設置文本顏色
DrawText(ps.hdc, L"朋友,你好。",-1, &(ps.rcPaint), DT_CENTER);
EndPaint(hwnd, &ps);
}
return 0;
~~~

如果想一次性繪制多個字符串,可以調用PolyTextOut函數,但這個函數在繪制出來的字符上好像有點問題。
~~~
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
SetTextColor(ps.hdc, RGB(10, 0, 255));//設置文本顏色
DrawText(ps.hdc, L"朋友,你好。",-1, &(ps.rcPaint), DT_CENTER);
// 用于設置每個字符間隔的數組
int arr1[2]= {45,0};
int arr2[3] = { 35, 40, 0 };
int arr3[2] = { 32, 0 };
POLYTEXT polys[] = { {2,2,3,L"大家",ETO_CLIPPED,ps.rcPaint,&arr1[0]},
{2,25,3,L"新年好",ETO_CLIPPED,ps.rcPaint,&arr2[0]},
{30,60,3,L"快樂\0",ETO_CLIPPED,ps.rcPaint,&arr3[0]}
};
PolyTextOut(ps.hdc, &polys[0],3);
EndPaint(hwnd, &ps);
}
return 0;
~~~

當然也可以填充一些圖形區域。
~~~
// 填充圖形
// 創建畫刷
HBRUSH hb = CreateSolidBrush(RGB(0,255,0));
// 畫刷選擇到當前DC中
HBRUSH orgBrs = (HBRUSH)SelectObject(ps.hdc, hb);
// 填充圖形
Ellipse(ps.hdc,135,35,202,170);
// 選回原先的畫刷
SelectObject(ps.hdc, orgBrs);?? ?
?? DeleteObject(hb); //清理對象
~~~

這樣我們就在窗口上畫了一個橢圓了。
響應WM_PAINT消息進行繪圖,應按照以下步驟:
**1、聲明一個PAINTSTRUCT結構體的變量,用于被填充與繪圖相關的信息。**
**2、BeginPaint,函數調用后開始畫圖。**
**3、畫完之后調用EndPaint函數,HDC會被自動釋放。**
- 前言
- (1):關于C++的幾個要點
- (2):完整的開發流程
- (3):窗口的重繪
- (4):創建菜單
- (5):具有單選標記的菜單
- (6):創建右鍵菜單
- (7):多邊形窗口
- (8):繪圖(A)
- (9):繪圖(B)
- (10):繪圖(C)
- (11):使用控件——先來耍一下按鈕
- (12):使用控件——單選按鈕
- (13):握手對話框
- (14):用對話框作為主窗口
- (15):ListView控件
- (16):ListView的多個視圖
- (17):啟動和結束進程
- (18):使用對話框的兩個技巧
- (19):瀏覽和打開文件
- (20):瀏覽文件夾
- (21):復制&amp;粘貼&amp;剪貼板操作
- (22):抓取屏幕
- (23):漸變顏色填充
- (24):計時器
- (25):監視剪貼板