1. 程式人生 > >關於WM_PAINT訊息中只重繪無效區的問題

關於WM_PAINT訊息中只重繪無效區的問題

一直以來我都有一個疑問,那就是下面的程式碼

	case WM_PAINT :
		hdc = BeginPaint (hwnd, &ps);
                ...............
		EndPaint (hwnd, &ps) ;
		return 0 ;

其中的BeginPaint(hwnd,&ps)通過ps結構體中的一個矩形結構體變數標識的無效區來重繪視窗,而且重點是隻重繪無效區。

那麼如果我程式碼中的省略處的程式碼在整個視窗上繪製,難道視窗無效時傳送WM_PAINT訊息就只重繪無效區嗎?

Windows系統真可謂博大精深,想要了解個透徹真可謂不易啊!不過我一直都在努力著去了解的更深刻,菜鳥不停的飛,總有一天會飛成老鳥的,因為歲月不饒人嗎!嘿嘿。

學習Windows程式設計的過程中總會有這樣那樣的疑問,我那愚鈍的腦袋一時半會真的很難解決,不過人家系統那樣搞自然會有它的依據,只是現在自己知識淺薄,看不透人家那樣做的原因。所以把遇到的問題記錄下來,好讓頓悟的那天有個翻查記錄的機會。

也許你也有同樣的困惑,但是人家系統確實是那樣做的。不信,咱們拿一個程式試試,便一切都瞭然了

#include <windows.h>

#define ID_TIMER    1

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT ("Beeper1") ;
	HWND         hwnd ;
	MSG          msg ;
	WNDCLASS     wndclass ;
	
	wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
	wndclass.lpfnWndProc   = WndProc ;
	wndclass.cbClsExtra    = 0 ;
	wndclass.cbWndExtra    = 0 ;
	wndclass.hInstance     = hInstance ;
	wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
	wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
	wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
	wndclass.lpszMenuName  = NULL ;
	wndclass.lpszClassName = szAppName ;
	
	if (!RegisterClass (&wndclass))
	{
		MessageBox (NULL, TEXT ("Program requires Windows NT!"), 
			szAppName, MB_ICONERROR) ;
		return 0 ;
	}
	
	hwnd = CreateWindow (szAppName, TEXT ("Beeper1 Timer Demo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL) ;
	
	ShowWindow (hwnd, iCmdShow) ;
	UpdateWindow (hwnd) ;
	
	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg) ;
		DispatchMessage (&msg) ;
	}
	return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BOOL fFlipFlop = FALSE ;
	HBRUSH      hBrush ;
	HDC         hdc ;
	PAINTSTRUCT ps ;
	RECT        rc, rect;

	rect.left	= 100;   //在這裡設定一個矩形區域,當做無效區
	rect.top	= 100;
	rect.right	= 500;
	rect.bottom	= 500;
	
	switch (message)
	{
	case WM_CREATE:
		SetTimer (hwnd, ID_TIMER, 1000, NULL) ;
		//這裡設定一個定時器,每隔1s傳送一個WM_TIMER訊息
		return 0 ;
		
	case WM_TIMER :
		MessageBeep (-1);          
		fFlipFlop = !fFlipFlop ;

		InvalidateRect (hwnd, &rect, FALSE);
                //這裡我們在每一次接受一個WM_TIMER訊息時,就通過這個呼叫使rect標識的矩形區域標識為無效
                //窗口出現無效區時會向訊息佇列中傳送WM_PAINT
                //按照hdc = BeginPaint (hwnd, &ps);會使無效區域有效,也即是通過只重繪無效區域使視窗變的有效
		//我們在WM_PAINT訊息中呼叫FillRect (hdc, &rc, hBrush);讓rc標識整個視窗客戶區,也就是繪製整個視窗客戶區
                //我們通過fFlipFlop來決定繪製藍色還是紅色
                //執行程式你發現視窗中只在左上角的標識的無效矩形區域出現紅色,其它區域永遠都是藍色
                //由此可以說明WM_PAINT訊息中確實只重繪無效區
                return 0 ;
		
	case WM_PAINT :
		hdc = BeginPaint (hwnd, &ps) ;
		
		GetClientRect (hwnd, &rc) ;
		hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;
		//Sleep(1000);
		FillRect (hdc, &rc, hBrush) ;
		EndPaint (hwnd, &ps) ;
		DeleteObject (hBrush) ;
		return 0 ;

/*通過這段程式碼可以測試WM_TIMER訊息的優先順序比較低
	case WM_LBUTTONDOWN:
		Sleep(5000);	//WM_TIMER訊息的優先順序比較低
		return 0 ;
*/

	case WM_DESTROY :
		KillTimer (hwnd, ID_TIMER) ;//清除定時器一定不能忘
		PostQuitMessage (0) ;
		return 0 ;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;
}


你也可以把程式碼拷貝到你的編譯器裡面試一試,看看究竟是怎樣的!