チェックボックス


チェックを操作する

今回は、ユーザーにブーリアン値を設定させるのに便利な
チェックボックスコントロールの操作について説明します

チェックボックスもボタンのスタイルの一種で
ボタンスタイルで BS_CHECKBOX を CreateWindow() で指定します
チェックボックスは、チェックボタンを左に置くか右に置くかを選択できます

デフォルトの状態で、ボタンは左に配置され
チェックボックスを表す文字は右側に配置されています
これを逆にするには BS_LEFTTEXT または BS_RIGHTBUTTON
同じく CreateWindow() 関数に指定すれば良いのです

ただし、BS_CHECKBOX は、押した時の強調表示以外の動作をサポートしません
ボタンを押したときのチェックしたり解除したりという動作はプログラムでサポートします
これが面倒な場合 BS_AUTOCHECKBOX を代わりに指定すれば
最低限の基本的な動作が、デフォルトでサポートされています
#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("Check") ,
			WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_LEFTTEXT ,
			0 , 0 , 100 , 30 ,
			hwnd , NULL , hInstance , NULL
	);

	CreateWindow(
			TEXT("BUTTON") , TEXT("Auto Check") ,
			WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX ,
			0 , 30 , 100 , 30 ,
			hwnd , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

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


BS_CHECKBOX のボタンコントロールは、クリックしてもチェックされません
また BS_LEFTTEXT によって文字が左側になっていることにも注目してください

下のチェックボックスは、BS_AUTOCHECKBOX です
こちらは、チェックボックスの基本的な動作がサポートされていますので
マウスでクリックすると、ボタンにチェックマークがつきます(もう一度押すと消える)

プッシュボタン同様に、チェックボックスもメッセージを送ることで操作します
チェックボックスをセットしたり解除したりするには BM_SETCHECK を使います
WPARAM に BST_CHECKED を指定するとボタンをチェックし
BST_UNCHECKED を指定すると、チェックを解除します
このメッセージの戻り値は 0 です

また、チェックボックスの状態を得ることも可能です
チェックボックスの状態を知るには BM_GETCHECK を使います
パラメータは全て 0 を指定します

このメッセージを送り SendMessage() の戻り値を調べることで
ボタンのチェック状態を知ることができます
BST_CHECKED ならばチェック状態、BST_UNCHECKED ならば非チェック状態です
#include <windows.h>

HWND check;

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_COMMAND:
		if(BST_CHECKED == SendMessage(check , BM_GETCHECK , 0 , 0))
			SendMessage(check , BM_SETCHECK , BST_UNCHECKED , 0);
		else
			SendMessage(check , BM_SETCHECK , BST_CHECKED , 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
	);

	check = CreateWindow(
			TEXT("BUTTON") , TEXT("Check") ,
			WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_LEFTTEXT ,
			0 , 0 , 100 , 30 ,
			hwnd , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}
チェックボックスがクリックされると、WM_COMMAND が発行されます
このとき、チェックボックスの状態を調べ、それに合わせてチェックを操作しています

ボタンがチェック状態であれば、チェックを外し
ボタンが非チェック状態であれば、ボタンをチェックするようにしています

しかし、じつは BST_CHECKED などによる調べ方は特別な場合で
2通りしかないチェックボックスの状態はブーリアンで表すこともできます
つまり、TRUE であればチェック状態で FALSE であれば非チェック状態なのです

もちろん BM_SETCHECK の WPARAM でもブーリアンで指定することができ
これを利用すれば、次のように簡単に先ほどのプログラムが実現できます
#include <windows.h>

HWND check;

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

	check = CreateWindow(
			TEXT("BUTTON") , TEXT("Check") ,
			WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_LEFTTEXT ,
			0 , 0 , 100 , 30 ,
			hwnd , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}
このように BM_GETCHECK の戻り値の否定をセットすることで
つねに、現在のチェック状態の逆をセットすることができます


3状態チェックボックス

チェックボックスには、もう一つ特殊なものが用意されています

通常のチェックボックスには、チェック状態か非チェック状態かの
ブーリアンを表すのに適した2通りの状態が存在していました
しかし、チェックボックスには3通りの状態を表すものもあります

これを使えば、チェック状態、曖昧な状態、非チェック状態、の3つに分けることができます
もちろん、ここで新しく加わった「曖昧な状態」を何に使うかは自由ですが
一般には「指定しない」とか「どちらでもよい」というような選択に使えそうです

//MSDN に indeterminate (はっきり言えない) とあるので
//このドキュメントでは、この状態を「曖昧な状態」と呼ぶことにする

曖昧な状態では、チェックマーすがついた状態で色がグレーになっています
この3状態チェックボックスは、ボタンスタイルで BS_3STATE を使います

基本的な仕様は通常のチェックボックスと同じですが
BM_SETCHECK や BM_GETCHECK で得られる値に
曖昧な状態を表す BST_INDETERMINATE が指定できます

BS_3STATE は、クリックされたときのチェック動作をサポートしませんが
クリックされるたびに状態を変化させる場合は BS_AUTO3STATE が便利です
これは、BS_AUTOCHECKBOX 同様に、最初から基本的な動作がサポートされています
#include <windows.h>

HWND check;

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_COMMAND:
		if(BST_INDETERMINATE == SendMessage(check , BM_GETCHECK , 0 , 0))
			SendMessage(check , BM_SETCHECK , BST_UNCHECKED , 0);
		else if(BST_UNCHECKED == SendMessage(check , BM_GETCHECK , 0 , 0))
			SendMessage(check , BM_SETCHECK , BST_CHECKED , 0);
		else
			SendMessage(check , BM_SETCHECK , BST_INDETERMINATE , 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
	);

	check = CreateWindow(
			TEXT("BUTTON") , TEXT("Check") ,
			WS_CHILD | WS_VISIBLE | BS_3STATE ,
			0 , 0 , 100 , 30 ,
			hwnd , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

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


上の図は、曖昧な状態のチェックボックスです
通常のチェックボックスは、定数を使わなくてもブール値を使って表すこともできましたが
3状態チェックボックスでは、例の定数を使って表現します
//実際には、曖昧な状態は数値の 2 で表されている




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