領域の消去と有効化


描画要求

イベントなどによって何らかのパラメータに変更があったとき
ウィンドウ全体、または一部を再描画したいときがあります

このような場合は WM_PAINT を呼び出せば良いのですが
WM_PAINT が呼び出されるのは無効リージョンが存在する時です

そこで、プログラム側で明示的に無効領域を作ることで
WM_PAINTを生成する、つまり領域を描きなおすことができるようになります
これを行うには InvalidateRect() 関数を用います

BOOL InvalidateRect(HWND hWnd , CONST RECT *lpRect , BOOL bErase);

hWnd には、無効化の対象となるウィンドウのハンドルを指定します
NULL を指定した場合、全てのウィンドウを無効化します

lpRect には、無効化する長方形をあらわす RECT 構造体のポインタを指定します
この引数をNULLにするとクライアント領域全体が対象になります

bErase を TRUE にすると BeginPaint() が呼び出されたときに背景を消去します
FALSE を指定すれば、背景はそのまま残ります
戻り値は、成功すれば 0 以外、失敗すれば 0 が返ります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	static int iCount = 0;
	static TCHAR strCount[8];

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_KEYDOWN:
		iCount++;
		InvalidateRect(hwnd , NULL , TRUE);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);

		wsprintf(strCount , "%d" , iCount);
		TextOut(hdc , 10 , 30 , strCount , lstrlen(strCount));

		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}
このプログラムを実行すると、キーボードを押すたびに
描画されている数次がインクリメントされていきます

キーボードが押されると WM_KEYDOWN メッセージが処理され
このメッセージでは単に iConde をインクリメントしているだけです
その後、InvalidateRect() によって無効領域を生成し、WM_PAINT を処理しています
そのため、キーボードを押すとウィンドウが更新されているように動作します

長方形以外に、リージョンを無効化させることもできます
リージョンの無効化には InvalidateRgn() を使います

BOOL InvalidateRgn(HWND hWnd , HRGN hRgn , BOOL bErase);

hWnd には、無効化の対象となるウィンドウのハンドル
hRgn には、無効化する領域をあらわすリージョンを指定しますが
これも InvalidateRect() と同様に NULL を指定すれば領域全体が対象になります

bErase は、InvalidateRect と同じです
戻り値は、常に0以外の値です
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HRGN hrgn;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_KEYDOWN:
		hdc = GetDC(hwnd);
		hrgn = CreateEllipticRgn(50 , 10 , 200 , 150);

		SelectObject(hdc , GetStockObject(WHITE_PEN));
		Rectangle(hdc , 10 , 10 , 400 , 200);
		InvalidateRgn(hwnd , hrgn , TRUE);

		DeleteObject(hrgn);
		ReleaseDC(hwnd , hdc);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);

		SelectObject(hdc , GetStockObject(BLACK_BRUSH));
		Rectangle(hdc , 10 , 10 , 400 , 200);

		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}


通常に WM_PAINT が呼び出されると黒い長方形を描画します
WM_PAINT はつねに黒い長方形を描画しようとしつづけています

しかし WM_KEYDOWN で InvalidateRgn() が呼び出されたときは
まず、WM_KEYDOWN の処理で長方形が白にクリアされます
その後、楕円のリージョンによる無効リージョンを生成してWM_PAING が処理されます
このとき、WM_PAINT が処理する領域は無効領域だけなので楕円だけが描画されるというわけです

これと対照的なファンクションで、無効領域を有効化するものもあります
長方形の有効化は ValidateRect() を用います

BOOL ValidateRect(HWND hWnd , CONST RECT *lpRect);

hWnd には、対象となるウィンドウのハンドルを指定します
ここに NULL を指定すると、全てのクライアント領域を無効化して再描画します

lpRect は、有効化する長方形を指定します
NULLを指定すると領域全体が有効化します
戻り値は、関数が成功すると 0以外、失敗すると 0 が返ります

他にも、リージョンを有効化する ValidateRgn() ファンクションもあります

BOOL ValidateRgn(HWND hWnd , HRGN hRgn);

hWnd には対象となるウィンドウのハンドルを
hRgn には、有効化するリージョンのハンドルを指定します
NULL を指定すれば、領域全体が対象になります

戻り値は、関数が成功すると 0 以外、失敗すると 0 が返ります
これを用いれば、奇妙だが次のようにプログラムすることもできる
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	static CONST LPCTSTR lpstr = TEXT("Kitty on your lap");

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		ValidateRect(hwnd , NULL);
		hdc = GetDC(hwnd);
		TextOut(hdc , 10 , 10 , lpstr , lstrlen(lpstr));
		ReleaseDC(hwnd , hdc);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}
WM_PAINT が呼び出されると否応なく全体を有効化し
(このとき、無効リージョンの範囲は気にしない)
その後に、GetDC() によってデバイスコンテキストのハンドルを得て描画しています

もちろん、一般的なプログラムではこのようなことをする必要はありません


InvalidateRect()

BOOL InvalidateRect(HWND hWnd , CONST RECT *lpRect , BOOL bErase);

指定された長方形を更新領域として追加する

hWnd - ウィンドウのハンドルを指定。NULL の場合、全てのウィンドウを無効化します
lpRect - RECT 構造体のポインタを指定。NULL の場合クライアント領域全体が対象
bErase - TRUE なら BeginPaint() 実行時に背景を消去、FALSE ならそのまま

戻り値 - 成功すれば 0 以外、失敗すれば 0

InvalidateRgn()

BOOL InvalidateRgn(HWND hWnd , HRGN hRgn , BOOL bErase);

指定されたリージョンを更新領域として追加する

hWnd - ウィンドウのハンドルを指定。NULL の場合、全てのウィンドウを無効化します
lpRgn - リージョンのハンドルを指定。NULL の場合クライアント領域全体が対象
bErase - TRUE なら BeginPaint() 実行時に背景を消去、FALSE ならそのまま

戻り値 - 常に 0 以外

ValidateRect()

BOOL ValidateRect(HWND hWnd , CONST RECT *lpRect);

指定された長方形を更新領域から削除する

hWnd - ウィンドウのハンドルを指定。NULL の場合、全てのウィンドウを無効化します
lpRect - RECT 構造体のポインタを指定。NULL の場合クライアント領域全体が対象

戻り値 - 成功すれば 0 以外、失敗すれば 0

ValidateRgn()

BOOL ValidateRgn(HWND hWnd , HRGN hRgn);

指定されたリージョンを更新領域から削除する

hWnd - ウィンドウのハンドルを指定
lpRgn - リージョンのハンドルを指定。NULL の場合クライアント領域全体が対象

戻り値 - 成功すれば 0 以外、失敗すれば 0



前のページへ戻る次のページへ