EternalWindows
コントロール基礎 / コントロールの状態

コントロールに対して何らかの操作を行うためには、 そのコントロールが有効になっている必要があります。 無効になっているコントロールは、たとえばボタンなら灰色に表示され、 ユーザーがボタンを押下してもアプリケーションにWM_COMMANDが通知されることはありません。 CreateWindowExのウインドウスタイルにWS_DISABLEDを指定していない場合は、 コントロールは有効の状態で作成されることになります。

無効になったコントロールは使用できなくなるわけですが、 アプリケーションによってはこれを望むような場面も多いといえます。 たとえば、特定の処理を実行している間に他の処理の実行を開始するボタンを無効にしたいことがあります。 このように、動的にコントロールの状態を変更したい場合は、EnableWindowを呼び出します。

BOOL EnableWindow(
  HWND hWnd,
  BOOL bEnable
);

hWndは、ウインドウハンドルを指定します。 bEnableは、ウインドウを有効にする場合に0以外の値を指定し、無効にする場合に0を指定します。

コントロールの状態変更は何かをきっかけに行われるものですから、 そのきっかけを与える例として、チェックボックスの使用方法について取り上げます。 チェックボックスを作成する場合は、ウインドウスタイルにBS_AUTOCHECKBOXを指定します。

CreateWindowEx(0, TEXT("BUTTON"), TEXT("チェックボックス"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, ...);

チェックボックスはボタンの一種であるため、第2引数のウインドウクラス名はBUTTONで構いません。 アプリケーションからチェックボックスにチェックを付けたい場合は、CheckDlgButtonを呼び出します。

BOOL CheckDlgButton(
  HWND hDlg,
  int nIDButton,
  UINT uCheck
);

hDlgは、親ウインドウのハンドルを指定します。 nIDButtonは、チェックしたいコントロールのIDを指定します。 hWndは、ウインドウハンドルを指定します。 uCheckは、チェックに関する定数を指定します。 BST_CHECKEDを指定した場合はチェックされ、BST_UNCHECKEDを指定した場合はチェックが外されます。

チェックボックスにチェックが付ついているかを確認したい場合は、 IsDlgButtonCheckedを呼び出します。

UINT IsDlgButtonChecked(
  HWND hDlg,
  int nIDButton
);

hDlgは、親ウインドウのハンドルを指定します。 nIDButtonは、チェックを確認したいコントロールのIDを指定します。 戻り値がBST_CHECKEDの場合は、チェックされていることを意味します。

今回のプログラムは、チェックボックスにチェックが付ついていない場合にボタンを無効化します。

#include <windows.h>

#define ID_BUTTON   100
#define ID_CHECKBOX 200

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndButton = NULL;

	switch (uMsg) {
		
	case WM_CREATE:
		hwndButton = CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 30, 30, 80, 40, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタンを有効化する"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 30, 80, 170, 40, hwnd, (HMENU)ID_CHECKBOX, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CheckDlgButton(hwnd, ID_CHECKBOX, BST_CHECKED);
		return 0;

	case WM_COMMAND: {
		int nId = LOWORD(wParam);

		if (nId == ID_BUTTON)
			MessageBox(NULL, TEXT("ボタンが押されました。"), TEXT("OK"), MB_OK);
		else if (nId == ID_CHECKBOX)
			EnableWindow(hwndButton, IsDlgButtonChecked(hwnd, ID_CHECKBOX) == BST_CHECKED);
		else
			;
		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

ボタンを有効にするという名前のチェックボックスからチェックを外した場合、 表示されているボタンは無効になります。 また、チェックを付けるとボタンは有効になります。 これを行っているのは、WM_COMMANDの次の処理です。

else if (nId == ID_CHECKBOX)
	EnableWindow(hwndButton, IsDlgButtonChecked(hwnd, ID_CHECKBOX) == BST_CHECKED);

ボタンの状態を変更するので、EnableWindowの第1引数にはボタンのウインドウハンドルを指定します。 第2引数は、ボタンを有効にするかどうかを指定しますが、これはチェックボックスにチェックが付いているかどうかで変化するので、 IsDlgButtonCheckedの戻り値がBST_CHECKEDであるかどうかを確認します。 BST_CHECKEDである場合は条件式が1になりますから、ボタンは有効になります。 一方、戻り値がBST_CHECKEDでない場合は条件式が0になりますから、ボタンは無効になります。

ラジオボタンの使用

今回のプログラムはチェックボックスを使用していましたが、 ラジオボタンを使用するよう変更したコードを次に示します。

#include <windows.h>

#define ID_BUTTON 100
#define ID_RADIO1 200
#define ID_RADIO2 300

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndButton = NULL;

	switch (uMsg) {
		
	case WM_CREATE:
		hwndButton = CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 30, 30, 80, 40, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタンを有効にする"), WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 30, 80, 170, 40, hwnd, (HMENU)ID_RADIO1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタンを無効にする"), WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 30, 120, 170, 40, hwnd, (HMENU)ID_RADIO2, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CheckDlgButton(hwnd, ID_RADIO1, BST_CHECKED);
		return 0;

	case WM_COMMAND: {
		int nId = LOWORD(wParam);

		if (nId == ID_BUTTON)
			MessageBox(NULL, TEXT("ボタンが押されました。"), TEXT("OK"), MB_OK);
		else if (nId == ID_RADIO1)
			EnableWindow(hwndButton, TRUE);
		else if (nId == ID_RADIO2)
			EnableWindow(hwndButton, FALSE);
		else
			;
		return 0;
	}

	case WM_CTLCOLORSTATIC:
		return (LRESULT)GetStockObject(WHITE_BRUSH);

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

ラジオボタンを作成するには、ウインドウスタイルにBS_AUTORADIOBUTTONを指定します。 上記コードでは、ラジオボタンを2つ作成していますが、 ラジオボタンの性質上、実際に選択できるラジオボタンは1つだけです。 有効にするラジオボタンが選択された場合はEnableWindowにTRUEを指定し、 無効にするラジオボタンが選択された場合はEnableWindowにFALSEを指定しています。

WM_CTLCOLORSTATICは、スタティックコントロールやチェックボックス、グループボックスに送られ、 ブラシのハンドルを返すことができます。 ここで返したブラシは、実際にコントロールの背景色に反映されますから、 背景色を変更したい場合はこのメッセージを処理することになります。



戻る