ラジオボタン


排他的なボタン

ラジオボタンとは、排他的なボタンでカーラジオのボタンに似ていることから由来します
基本的な動作はチェックボックスと同じですが、排他的にたのボタンと関係し合うところが
ラジオボタンの最大の特徴です

ラジオボタンは、いくつかのボタンが一つのグループとなって作用します
一つのボタンが必ず押されている状態で、かつ選べるボタンは一つまでなのです
あるボタンが選択されれば、他のボタンは全て解除されることになります

ラジオボタンは、CreateWindow() 関数のスタイルに BS_RADIOBUTTON を指定し
WM_COMMAND を処理し、BM_SETCHECK などを使ってオン、オフを操作します
チェックボックスやこれまでのことがわかっていれば、難しい話ではありません
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND radio;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		radio = CreateWindow(
			TEXT("BUTTON") , TEXT("Kitty") ,
			WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON ,
			0 , 0 , 100 , 30 ,
			hwnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		return 0;
	case WM_COMMAND:
		SendMessage(radio , BM_SETCHECK ,
			!SendMessage(radio , 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
	);

	if (hwnd == NULL) return -1;

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


ラジオボタンが出現しましたね
このコントロールのチェック動作は WM_COMMAND で処理しています
このプログラムでは、ラジオボタンがチェックボックスのように動作することが確認できます

ですが、本来のラジオボタンの目的は常に一つのボタンがチェックされており
複数のボタンを同時に選択できないというようにプログラムしなければなりません
この動作は、簡単なアルゴリズムで実現することができるのはわかると思います
しかし、面倒な場合は CreateWindow() 関数で BS_AUTORADIOBUTTON を設定すれば
最低限のラジオボタンとしての動作をサポートしているコントロールになります

BS_AUTORADIOBUTTON は、ユーザーがあるボタンを選択すると
そのボタンをチェックし、他の同じグループに所属するラジオボタンのチェックを外します
通常は、後記するダイアログボックス内で使われます
#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:
		CreateWindow(
			TEXT("BUTTON") , TEXT("Kitty") ,
			WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON ,
			0 , 0 , 100 , 30 ,
			hwnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		CreateWindow(
			TEXT("BUTTON") , TEXT("Kitty") ,
			WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON ,
			0 , 30 , 100 , 30 ,
			hwnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		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;
}


二つのラジオボタンは同じグループにあります
一方のボタンをチェックすると、もう一方のボタンは解除されます
また、チェックされているボタンをクリックしてもトグルのように解除されることはありません


グループ化

関連するラジオボタンをグループ化するには
CreateWindow() 関数で WS_GROUP スタイルを設定します

WS_GROUP スタイルが設定されているコントロールは
そこがグループの開始点だと解釈されます
この場合、次の WS_GROUP が設定されているコントロールまでが 1 つのグループとなります

s#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:
		CreateWindow(
			TEXT("BUTTON") , TEXT("Radio Button 1") ,
			WS_CHILD | WS_VISIBLE | WS_GROUP |
			BS_AUTORADIOBUTTON ,
			0 , 0 , 200 , 30 ,
			hwnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		CreateWindow(
			TEXT("BUTTON") , TEXT("Radio Button 2") ,
			WS_CHILD | WS_VISIBLE |
			BS_AUTORADIOBUTTON ,
			0 , 30 , 200 , 30 ,
			hwnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		CreateWindow(
			TEXT("BUTTON") , TEXT("Radio Button 3") ,
			WS_CHILD | WS_VISIBLE | WS_GROUP |
			BS_AUTORADIOBUTTON ,
			0 , 60 , 200 , 30 ,
			hwnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		CreateWindow(
			TEXT("BUTTON") , TEXT("Radio Button 4") ,
			WS_CHILD | WS_VISIBLE |
			BS_AUTORADIOBUTTON ,
			0 , 90 , 200 , 30 ,
			hwnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		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;
}
このプログラムは、4 つのラジオボタンをウィンドウに追加し
4 つのボタンを 2 つのグループに WS_GROUP スタイルを使って分割しています

ラジオボタンには BS_AUTORADIOBUTTON が指定されているため
グループ内で選択できるラジオボタンは、自動的に 1 つだけに制限されます
ボタンの選択関係を見れば、ボタンが 2 つのグループに分割されていることが確認できるでしょう



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