ダイアログの内容


ダイアログコントロール

前回は、ダイアログの表示方法と基本的な技術
それに、ダイアログウィンドウの外観やスタイルについて説明しました
今回は、これに加えて肝心なダイアログの内容について解説したいと思います

ダイアログに文字を表示するには、static コントロールを貼り付ける形になります
リソーススクリプトで CTEXTLTEXTRTEXT 文のいずれかを使えます
これらは、それぞれ順に SS_CENTER、SS_LEFT、SS_RIGHT スタイルを持つコントロールです

CTEXT text, id, x, y, width, height [, style [, extended-style]]
LTEXT text, id, x, y, width, height [[, style [[, extended-style]]]]
RTEXT text, id, x, y, width, height [[, style [[, extended-style]]]]

text には、表示する文字列を指定します
id はこのコントロールの ID を16ビット値で指定します

x、y には、コントロールの位置をそれぞれ X座標、Y座標で指定します
width はコントロールの幅、height は高さを指定します
この時、座標系はピクセルではないことを忘れないで下さい

style にはウィンドウスタイルを | を用いて組み合わせて指定できます
extended-style には、拡張ウィンドウスタイルを指定することができます
/*リソーススクリプト*/
KITTY DIALOG 10 , 10 , 100 , 50
FONT 12 , "MS Shell Dlg"
CAPTION "Magical nyan nyan TARUTO" {
	LTEXT "Kitty on your lap" , -1, 2 , 0 , 70 , 10
	CTEXT "Kitty on your lap" , -1 , 2 , 10 , 70 , 10
	RTEXT "Kitty on your lap" , -1 , 2 , 20 , 70 , 10
}
#include <windows.h>

BOOL CALLBACK DialogProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_CLOSE:
		EndDialog(hwnd , IDOK);
		return TRUE;
	}
	return FALSE;
}

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONUP:
		DialogBox(
			(HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
			TEXT("KITTY") , hwnd , DialogProc
		);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	HACCEL haccel;
	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;
}


文字列が表示されましたね
ダイアログボックスが、コントロールで構成されるという概念も理解できたと思います

基本的なダイアログの多くは、ボタンコントロールを持っています
通常のボタンは PUSHBUTTON 文を
WS_DEFPUSHBUTTON スタイルの場合は DEFPUSHBUTTON 文を使います

チェックボックスは CHECKBOX、ラジオボタンは RADIOBUTTON
グループボックスは GROUPBOX 文をそれぞれ用いることで表現できます

PUSHBUTTON text, id, x, y, width, height [[, style [[, extended-style]]]]
DEFPUSHBUTTON text, id, x, y, width, height [, style [, extended-style]]
CHECKBOX text, id, x, y, width, height [, style [, extended-style]]
RADIOBUTTON text, id, x, y, width, height [[, style [[, extended-style]]]]
GROUPBOX text, id, x, y, width, height [[, style [[, extended-style]]]]

各パラメータは、先ほどの static コントロールと同じです
先ほどのプログラムは、スタティックコントロールのため WM_COMMAND を使いませんでした
しかし、コントロールからメッセージを受ける場合は id パラメータが活躍します
/*リソーススクリプト*/
KITTY DIALOG 10 , 10 , 100 , 50
FONT 12 , "MS ゴシック"
CAPTION "Magical nyan nyan TARUTO" {
	LTEXT "終了します\nよろしいですか?" , -1 , 5 , 5 , 90 , 20
	PUSHBUTTON "OK" , IDOK , 40 , 35 , 20 , 10
	PUSHBUTTON "Cancel" , IDCANCEL , 65 , 35 , 30 , 10
}
#include <windows.h>

BOOL CALLBACK DialogProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_CLOSE:
		EndDialog(hwnd , IDCANCEL);
		return TRUE;
	case WM_COMMAND:
		if (LOWORD(wp) == IDOK) EndDialog(hwnd , IDOK);
		else EndDialog(hwnd , IDCANCEL);
		return TRUE;
	}
	return FALSE;
}

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CLOSE:
		if (DialogBox(
			(HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
			TEXT("KITTY") , hwnd , DialogProc
		) == IDOK) DestroyWindow(hwnd);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	HACCEL haccel;
	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;
}


ウィンドウを閉じようとすると、ダイアログが表示され確認を促します
この時、OK ボタンを押せば Dialog() 関数は IDOK を返し
ウィンドウプロシージャはそれを察知して DestroyWindow(hwnd) を呼び出します

ラジオボタンなどを実装する場合、少し工夫が必要になります
なぜならば、ダイアログの場合は私たちが CreateWindow() を使ったわけではないので
コントロールの全てのハンドルを持っていないのです

WM_COMMAND の場合は、選択されたコントロールのハンドルを持っています
しかし、ラジオボタンの場合は他のボタンのハンドルも必要になることがあります
他のボタンのチェックを外して単一のボタンのみをチェックする場合などです
このような場合は GetDlgItem() 関数を用います

HWND GetDlgItem(HWND hDlg , int nIDDlgItem);

hDlg には、ダイアログボックスのハンドルを
nIDDlgItem には、コントロールの ID を指定します
戻り値は、コントロールのハンドルですが失敗すると NULL です

この方法を使えば、ループなどで同一グループのラジオボタンのチェックを全て外し
選択されたラジオボタンをチェックするよう SendMessage() を送って処理できます

もし、GetDlgItem() を使ってハンドルをストックする必要がないならば
むしろ SendDlgItemMessage() 関数を使ったほうが早いでしょう
この関数は、ダイアログ内の指定 ID のコントロールにメッセージを送ります
LONG SendDlgItemMessage(
	HWND hDlg , int nIDDlgItem ,
	UINT Msg,
	WPARAM wParam , LPARAM lParam
);
hDlg には、ダイアログのハンドルを、nIDDDlgItem には対象のコントロール ID
Msg には送るメッセージ、wParam と lParam には追加情報を指定します
戻り値は、メッセージの処理結果になります

これで、ダイアログのあらゆるコントロールに対して
CreateWindow() で作ったコントロールの様に自由に操作することができます
/*リソーススクリプト*/
KITTY DIALOG 10 , 10 , 100 , 50
FONT 12 , "MS ゴシック"
CAPTION "Magical nyan nyan TARUTO" {
	RADIOBUTTON "RENA" , 1 , 5 , 5 , 40 , 10
	RADIOBUTTON "YUKI" , 2 , 5 , 15 , 40 , 10
	RADIOBUTTON "MIMI" , 3 , 5 , 25 , 40 , 10
}
#include <windows.h>

BOOL CALLBACK DialogProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	int iCount;

	switch (msg) {
	case WM_CLOSE:
		EndDialog(hwnd , IDOK);
		return TRUE;
	case WM_COMMAND:
		for (iCount = 1 ; iCount < 4 ; iCount++)
			SendDlgItemMessage(
				hwnd , iCount , BM_SETCHECK ,
				iCount == LOWORD(wp) , 0
		);
		return TRUE;
	}
	return FALSE;
}

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONUP:
		DialogBox(
			(HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
			TEXT("KITTY") , hwnd , DialogProc
		);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	HACCEL haccel;
	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;
}


for ループで、ラジオボタンを操作しているのがわかります
ループでは、ループカウンタと ID を対応させたほうが簡単です
iCount == LOWORD(wp) とすることで、選択されたコントロールをチェックしています

しかし、連続した ID を持つラジオボタンであれば
これよりもはるかに簡素な CheckRdioButton() 関数が有効です
BOOL CheckRadioButton(
	HWND hDlg ,
	int nIDFirstButton , int nIDLastButton ,
	int nIDCheckButton
);
hDlg は、ラジオボタンを持つダイアログボックスのハンドルを
nIDFirstButton は最初のラジオボタンの ID
nIDLastButton は、最後のラジオボタンの ID を指定します
nIDCheckButton は、このうち選択するラジオボタンを指定します

関数が成功すると 0 以外、失敗すると 0 が返ります
チェックボタンならば CheckDlgButton() 関数を使えばよい

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

hDlg は、ボタンを持つダイアログボックスのハンドルを
nIDButton は、操作対象のボタンの ID を指定します
uCheck にはチェック状態を変更する以下の定数のいずれかを指定します

定数解説
BST_CHECKED ボタンをチェックされた状態にします
BST_INDETERMINATE ボタンをグレイ表示 (不確定) の状態にします
ボタンが、BS_3STATEまたは BS_AUTO3STATE スタイルを
持つときにだけ使用してください
BST_UNCHECKED ボタンをチェックされていない状態にします

戻り値は、関数が成功すると 0 以外、失敗すると 0 が返ります
チェックボックスのチェック状態を知るには IsDlgButtonChecked() を使います

UINT IsDlgButtonChecked(HWND hDlg , int nIDButton);

hDlg には、ボタンを持つダイアログのハンドルを
nIDButton は、調べるボタンの ID を指定します
ボタンがチェック可能なものであれば、以下の定数が返ります

定数解説
BST_CHECKED ボタンをチェックされた状態である
BST_INDETERMINATE ボタンをグレイ表示 (不確定) の状態である
BST_UNCHECKED ボタンをチェックされていない状態である

指定したボタンがチェックできないスタイルなら 0 が返ります
/*リソーススクリプト*/
KITTY DIALOG 10 , 10 , 100 , 50
FONT 12 , "MS ゴシック"
CAPTION "Magical nyan nyan TARUTO" {
	RADIOBUTTON "RENA" , 1 , 5 , 5 , 40 , 10
	RADIOBUTTON "YUKI" , 2 , 5 , 15 , 40 , 10
	RADIOBUTTON "MIMI" , 3 , 5 , 25 , 40 , 10

	CHECKBOX "KAIMU" , 4 , 50 , 5 , 40 , 10
}
#include <windows.h>

BOOL CALLBACK DialogProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	int iCount;

	switch (msg) {
	case WM_CLOSE:
		EndDialog(hwnd , IDOK);
		return TRUE;
	case WM_COMMAND:
		if (LOWORD(wp) < 4)
			CheckRadioButton(hwnd , 1 , 3 , LOWORD(wp));
		else CheckDlgButton(hwnd , 4 , 
			!(IsDlgButtonChecked(hwnd , 4) == BST_CHECKED)
		);
		return TRUE;
	}
	return FALSE;
}

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONUP:
		DialogBox(
			(HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
			TEXT("KITTY") , hwnd , DialogProc
		);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	HACCEL haccel;
	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;
}


こうすれば、ラジオボタンもチェックボックスも簡単に実装できます

また、ダイアログボックスはアイコンを配置することも簡単に行えます
アプリケーションのバージョン情報などで、よく見かけますね
アイコンの配置には ICON コントロールを用います

ICON text, id, x, y, [[width, height, style [[, extended-style]]]]

この文を使う場合は、ICON 文でアイコンを指定する必要があります
text には、ICON 文で定義した ID を指定します
x には表示する X座標、y には Y座標をそれぞれ指定します
width と height は無視されるため 0 を指定しなければなりません

style にはスタイル、extended-style は拡張スタイルを指定します
width と height は無視されますが省略はできません
/*リソーススクリプト*/
KITTY ICON "test.ico"

KITTY DIALOG 10 , 10 , 100 , 50
FONT 12 , "MS ゴシック"
CAPTION "Magical nyan nyan TARUTO" {
	ICON "KITTY" , 0 ,  10 , 10 , 0 , 0
}
#include <windows.h>

BOOL CALLBACK DialogProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_CLOSE:
		EndDialog(hwnd , IDOK);
		return TRUE;
	}
	return FALSE;
}

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONUP:
		DialogBox(
			(HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
			TEXT("KITTY") , hwnd , DialogProc
		);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	HACCEL haccel;
	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;
}


バージョン情報などでアイコンを表示させる場合
この方法をもいれば、よりダイアログを飾ることができます


GetDlgItem()

HWND GetDlgItem(HWND hDlg , int nIDDlgItem);

ダイアログボックスから、指定したコントロールのハンドルを取得します

hDlg - ダイアログボックスのハンドルを指定します
nIDDlgItem - コントロールの識別子を指定します

戻り値 - コントロールのハンドル。失敗すると NULL

SendDlgItemMessage()

LONG SendDlgItemMessage(
	HWND hDlg , int nIDDlgItem ,
	UINT Msg,
	WPARAM wParam , LPARAM lParam
);
ダイアログの指定コントロールにメッセージを送ります

hDlg - ダイアログボックスのハンドルを指定します
nIDDlgItem - コントロールの ID を指定します
Msg - メッセージを指定します
wParam - 追加情報を指定します
lParam - 追加情報を指定します

戻り値 - メッセージの処理結果が返ります

CheckRdioButton()

BOOL CheckRadioButton(
	HWND hDlg ,
	int nIDFirstButton , int nIDLastButton ,
	int nIDCheckButton
);
列挙されているオプションボタンのグループから
指定ボタンを選択し、そのほかのボタンを解除します

hDlg - ラジオボタンを持つダイアログボックスのハンドルを指定します
nIDFirstButton - 最初のラジオボタンの ID を指定します
nIDLastButton - 最後のラジオボタンの ID を指定します
nIDCheckButton - このうち選択するラジオボタンを指定します

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

CheckDlgButton()

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

指定したボタンのチェック状態を変更します

hDlg - ボタンを持つダイアログボックスのハンドルを指定します
nIDButton - 操作対象のボタンの ID を指定します
uCheck - チェック状態を変更する定数を指定します

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

uCheck には以下の定数のいずれかを指定します
定数解説
BST_CHECKED ボタンをチェックされた状態にします
BST_INDETERMINATE ボタンをグレイ表示 (不確定) の状態にします
ボタンが、BS_3STATEまたは BS_AUTO3STATE スタイルを
持つときにだけ使用してください
BST_UNCHECKED ボタンをチェックされていない状態にします

IsDlgButtonChecked()

UINT IsDlgButtonChecked(HWND hDlg , int nIDButton);

ボタンのチェック状態を取得します

hDlg - ボタンを持つダイアログのハンドルを指定します
nIDButton - 調べるボタンの ID を指定します

戻り値 - ボタンの状態を表す定数

戻り値は、以下の定数のいずれかになります

定数解説
BST_CHECKED ボタンをチェックされた状態である
BST_INDETERMINATE ボタンをグレイ表示 (不確定) の状態である
BST_UNCHECKED ボタンをチェックされていない状態である



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