ボタン


オーナーと子ウィンドウ

Windows は、特定の処理を専門とする動作をパッケージ化し
ひとつのウィンドウとして扱い、操作することができます

これまで多くのアプリケーションで見たことがあると思いますが
ボタンやチェックボックスなども全てウィンドウ(コントロール)なのです

ボタンなどのコントロールは CreateWindow() 関数がデフォルトで用意しています
これらの定義済みコントロールクラスは、WNDCLASS を設定する必要がありません
また、ボタンが押された時などの描画処理やメッセージの処理は
全てデフォルトの状態でサポートされているので、何も設定する必要がないのです

これらのコントロールは、トップのウィンドウの子ウィンドウとして登録します
そのためには、CreateWindow() 関数のスタイルに WS_CHILD を指定します
また、第8引数の hWndParent に、オーナーとなる親ウィンドウのハンドルを指定します
配置する位置は、オーナーウィンドウのクライアントエリアの座標になります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		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
	);

	CreateWindow(
		TEXT("BUTTON") , TEXT("Kitty") ,
		WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
		0 , 0 , 100 , 50 ,
		hwnd , NULL , hInstance , NULL
	);

	CreateWindow(
		TEXT("BUTTON") , TEXT("Kitty") ,
		WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON ,
		0 , 50 , 100 , 50 ,
		hwnd , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

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


このプログラムは、二つのボタンを表示します
上のボタンは、通常のプッシュボタンです。多くのアプリケーションでみかけますね
BS_DEFPUSHBUTTON もプッシュボタンの部類ですが、黒いふちを持ちます

しかし、この状態ではまだボタンを押しても何も起こりません
通常、ボタンなどのコントロールは押すことによって何らかの処理が求められます

ボタンは、押されると親ウィンドウのウィンドウプロシージャに WM_COMMAND を送ります
このメッセージは WPARAM の下位ワードに子ウィンドウの ID が格納されています
子ウィンドウのIDは CreateWindow() 関数の第9引数に指定します
通常、この引数はメニューのハンドルを指定しますが、子ウィンドウのIDを指定することもできるのです
ただし、その場合は HMENU 型に整数をキャストする必要があります

WPARAM の上位ワードにはコードが格納されています
コントロールからのメッセージであれば通知コードが
アクセラレータからであれば 1、メニューからであれば 0 が格納されています


この通知コードとは、通知メッセージ のことであり
まず、ボタンに何らかの変化がある場合は通知メッセージが送られます
親ウィンドウは、WM_COMMAND を通して通知メッセージを知り
コントロールがどのような状態なのかを判断し、適切な処理を行います

定数解説
BN_CLICKED ボタンをクリック
BN_DBLCLK ユーザーが BS_OWNERDRAW
または BS_RADIOBUTTON スタイルを持つ
ボタンをダブルクリックするとき
BN_DISABLE ボタンが使用不能
BN_DOUBLECLICKED BN_DBLCLK と同じ
BN_HILITE ユーザーがボタンを選択
BN_KILLFOCUS キーボードフォーカスを失った
ボタンは、この通知メッセージを送るために
BS_NOTIFY スタイルを持たなければならない
BN_PAINT ボタンを再描画しなければならない
BN_PUSHED BN_HILITE と同じ
BN_SETFOCUS キーボードフォーカスを得た
ボタンは、この通知メッセージを送るために
BS_NOTIFY スタイルを持たなければならない
BN_UNHILITE ハイライトがボタンから削除されなければならない
BN_UNPUSHED BN_UNHILITE と同じ

しかし、これらはほとんど使われることはありません

LPARAM には、子ウィンドウのハンドルが格納されています
このメッセージを処理した場合は 0 を返さなければなりません
#include <windows.h>

#define BUTTON_ID1 0
#define BUTTON_ID2 1

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_COMMAND:
		switch(LOWORD(wp)) {
		case BUTTON_ID1:
			MessageBox(hwnd , TEXT("BUTTON_ID1") , TEXT("Kitty") , MB_OK);
			break;
		case BUTTON_ID2:
			MessageBox(hwnd , TEXT("BUTTON_ID2") , TEXT("Kitty") , MB_OK);
			break;
		}
		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
	);

	CreateWindow(
		TEXT("BUTTON") , TEXT("Kitty") ,
		WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
		0 , 0 , 100 , 50 ,
		hwnd , (HMENU)BUTTON_ID1 , hInstance , NULL
	);

	CreateWindow(
		TEXT("BUTTON") , TEXT("Kitty") ,
		WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON ,
		0 , 50 , 100 , 50 ,
		hwnd , (HMENU)BUTTON_ID2 , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}
今度のプログラムは、ボタンを押すとメッセージボックスを表示します
ボタンが押されると WM_COMMAND メッセージが親ウィンドウのプロシージャに送られます
これを処理し、さらに ID を調べどちらのボタンが押されたのかを判別しています

また、生成されたコントロールは
プログラムが終了すると、親ウィンドウとともに破棄されます



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