位置と順序


ウィンドウの位置

通常、トップレベルのアプリケーションウィンドウを操作するのはユーザーです
これのサイズや位置にプログラムが関与する必要はありません
もっとも、DefWindowProc() を使わない場合は独自に処理する必要があります

コントロールの場合は、サイズや位置を調整するようなデフォルト処理はありません
こちらがプログラムしなければ、コントロールは常に静的です

ウィンドウの位置とサイズの変更には MoveWindow() 関数を使います
この関数は、スクリーン座標でウィンドウの長方形を指定します
BOOL MoveWindow(
	HWND hWnd,
	int X , int Y ,
	int nWidth , int nHeight ,
	BOOL bRepaint
);
hwnd は、ウィンドウのハンドルを
X と Y にはウィンドウの左端の X座標 と Y座標 をそれぞれ指定します
nWidth と nHeight はそれぞれウィンドウの横幅と高さを指定します

bRepaint が TRUE ならウィンドウを再描画し、FALSE なら再描画しません
再描画する場合は、WM_PAINT がウィンドウに送られてきます
関数が成功すると 0 以外、失敗すると 0 が返ります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hPush;
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		GetClientRect(hwnd , &rect);
		hPush = CreateWindow(
			TEXT("BUTTON") , TEXT("Kitty") ,
			WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
			0 , 0 , rect.right , rect.bottom ,
			hwnd , NULL , 
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		return 0;
	case WM_SIZE:
		MoveWindow(hPush , 0 , 0 , LOWORD(lp) , HIWORD(lp) , TRUE);
		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)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


ウィンドウのクライアント領域全体がボタンコントロールのプログラムです
エディタなどを作る場合、エディットコントロールはウィンドウサイズに合わせるものです
その場合は、このようにウィンドウの位置とサイズを操作する必要があります


Zオーダー

グラフィックシステムの GUI オブジェクトの便利さはすでに承知だろう
しかし、これを扱っているとある特有の問題にぶつかる
同レベルのコントロール同士が、互いに一部分の領域を共有する場合です

例えば、カードゲームを作る場合、カードはコントロールとして実装するだろう
普通は見栄えを考えて、カードは重ねた形を取るだろう
この時、クリックしたカードを最前面に出し、そうでないカードを後ろにするという処置を取る

このような、コントロールの前後ろの関係を Zオーダー (Z 順序)という
Z オーダーはコントロールだけでなくウィンドウにも当然存在します
これを操作するには SetWindowPos() 関数を使用します
BOOL SetWindowPos(
	HWND hWnd , HWND hWndInsertAfter ,
	int X , int Y , int cx , int cy ,
	UINT uFlags
);
hWnd は対象となるウィンドウのハンドル
hWndInsertAfter は、hWnd に先行するウィンドウを指定します
X , Y はウィンドウの左上隅の X , Y 座標を指定し
cx , cy はそれぞれウィンドウの幅と高さを指定します

uFlags はウィンドウの位置やサイズ変更に対する設定を定数で指定します
詳しい各定数と意味は下記のリファレンスを参照してください
この関数は MoveWindow() のようにウィンドウの位置とサイズを変更できますが
Z オーダーだけを目的に使う場合は SWP_NOMOVE と SWP_NOSIZE を指定することで
X , Y , cx , cy の各引数を無視させることができます

hWndInsertAfter にも、定数を指定することが可能です
HWND_TOP を指定すれば、Z オーダーの先頭にウィンドウが配置されます
さらに HWND_TOPMOST を指定すれば、常に最前面に置かれます

この関数の戻り値は成功すれば 0 以外、失敗すれば 0 です
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		SetWindowPos(
			hwnd , HWND_TOPMOST ,
			0 , 0 , 0 , 0 ,
			SWP_NOMOVE | SWP_NOSIZE
		);
		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)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


通常、トップレベルウィンドウの Z オーダーは
アクティブなウィンドウが最前面にくるということは常識でしょう
しかし、HWND_TOPMOST を指定してウィンドウを最前面ウィンドウにした場合
非最前面ウィンドウはアクティブになっても最前面ウィンドウの前には出ません


MoveWindow()

BOOL MoveWindow(
	HWND hWnd,
	int X , int Y ,
	int nWidth , int nHeight ,
	BOOL bRepaint
);
指定されたウィンドウの位置とサイズを変更します

hWnd - ウィンドウのハンドルを指定します
X - ウィンドウ左上隅の X 座標を指定します
Y - ウィンドウ左上隅の Y 座標を指定します
nWidth - ウィンドウの横幅を指定します
nHeight - ウィンドウの高さを指定します
bRepaint - TRUE だと再描画、FALSE だと再描画しません

戻り値 - 関数が成功すると 0 以外、失敗すると 0

SetWindowPos()

BOOL SetWindowPos(
	HWND hWnd , HWND hWndInsertAfter ,
	int X , int Y , int cx , int cy ,
	UINT uFlags
);
子ウィンドウ、ポップアップウィンドウ、トップレベルウィンドウにおいて
そのサイズ、位置、Z 順位を変更します

hWnd - ウィンドウのハンドルを指定します
hWndInsertAfter - hWnd 先行するウィンドウのハンドルを指定します
X - ウィンドウ左上隅の X 座標を指定します
Y - ウィンドウ左上隅の Y 座標を指定します
cx - ウィンドウの横幅を指定します
cy - ウィンドウの高さを指定します
uFlags - ウィンドウのサイズおよび位置の変更に関するフラグを指定します

戻り値 - 関数が成功すると 0 以外、失敗すると 0

hWndInsertAfter には次の定数を指定することもできます

定数解説
HWND_BOTTOM ウィンドウを Z 順位の最後 (一番下) に置きます
hWnd パラメータで指定したウィンドウが
最前面ウィンドウ (WS_EX_TOPMOST) のときは
そのウィンドウは最前面ウィンドウではなくなり
ほかのすべてのウィンドウの下に置かれます
HWND_NOTOPMOST ウィンドウを最前面ウィンドウ (WS_EX_TOPMOST) 以外の
すべてのウィンドウの上位 (すべての最前面のウィンドウの下位 ) に置きます
hWnd パラメータで指定したウィンドウが最前面ウィンドウでないときは
このフラグは意味を持ちません
HWND_TOP ウィンドウを Z 順位の最初 (一番上) に置きます
ただし、非最前面ウィンドウが最前面ウィンドウにされることはありません
HWND_TOPMOST ウィンドウを最前面ウィンドウにします

uFlags には、以下の定数を組み合わせて使うことができます

定数解説
SWP_DRAWFRAME ウィンドウを囲む (ウィンドウクラスの記述部分で定義されている)
枠を描画します
SWP_FRAMECHANGED たとえウィンドウのサイズが変更されなくても
ウィンドウに WM_NCCALCSIZE メッセージを送ります
このフラグを指定しない場合は、ウィンドウのサイズが変更されるときにだけ
WM_NCCALCSIZE メッセージが送られます
SWP_HIDEWINDOW ウィンドウを非表示にします
SWP_NOACTIVATE ウィンドウをアクティブ化しません
このフラグを指定しない場合は、ウィンドウはアクティブ化され
(hWndInsertAfter パラメータの設定によって) 最前面
または非最前面のどちらかのグループの最上位に移動します
SWP_NOCOPYBITS クライアント領域の内容全体を破棄します
このフラグを指定しない場合は、クライアント領域の有効な内容が保存され
ウィンドウの再配置後にクライアント領域にコピーし直されます
SWP_NOMOVE 現在の位置を維持します
(X パラメータと Y パラメータを無視します )
SWP_NOOWNERZORDER オーナーウィンドウの Z 順位を変えません
SWP_NOREDRAW 変更結果を再描画しません
どんな種類の再ペイントも発生しません
このフラグは、クライアント領域
非クライアント領域 (タイトルバーやスクロールバーを含む)
ウィンドウが移動した結果現れた親ウィンドウのどのような部分にも、適用されま
このフラグを指定した場合、アプリケーションは、ウィンドウや親ウィンドウの
再描画の必要な部分を明示的に無効化または再描画しなければなりません
SWP_NOREPOSITION SWP_NOOWNERZORDER フラグと同じです
SWP_NOSENDCHANGING ウィンドウに WM_WINDOWPOSCHANGING メッセージを
受け取らせないようにします
SWP_NOSIZE 現在のサイズを維持します
(cx パラメータと cy パラメータを無視します )
SWP_NOZORDER 現在の Z 順位を維持します
(hWndInsertAfter パラメータを無視します)
SWP_SHOWWINDOW ウィンドウを表示します




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