ダイアログ


ダイアログボックスを作る

ユーザーに何らかの小さな情報を提示したり
またはいくつかのオプションなどの設定を行わせる場合
専用のウィンドウを作るのは大げさであるが、メニューでは事足りぬことがあります

一般に、このような処理にはダイアログボックスが用いられます
ダイアログボックスは、これまでの様にリソーススクリプトで作成することができます
しかし、メニューやアイコンのように単純ではありません
とはいえ難しく考える必要もなく、それだけ機能が豊富であると前向きに考えてください

ダイアログを作るには、リソーススクリプトの DIALOG 文を使います
基本的な書式はこれまでと同じです

nameID DIALOG x, y, width, height [optional-statements] { control-statement . . . }

nameID には、ダイアログの識別子を指定します
アイコン同様に、文字列または16ビット数値を指定することができます

x には X座標、y には Y座標をそれぞれ
ウィンドウのクライアント座標でダイアログの左上の位置を指定します
width は横幅、height は縦幅になります

ただし、ダイアログで指定する座標、サイズはピクセルではないので注意してください
ダイアログの場合、座標系は文字サイズを基準としているのです
水平サイズは平均文字幅の 1/4、垂直サイズは文字高の 1/8 を単位に
ダイアログボックスフォントの文字サイズを基準として使用されます

この仕様は解像度やフォントの影響を受けずに
ダイアログの外見やサイズなどのレイアウトを一定に保つことができるメリットがあります
一般にフォントの幅は高さの 1/2 なので、水平、垂直の単位はほぼ同じになります

optional-statements には、ダイアログの各種オプションを指定します
オプションは、これから少しずつ紹介していきますが
座標系の単位に影響するフォントは FONT ステートメントを使います

FONT pointsize, "typeface"

pointsize は、フォントサイズを数値で指定します
typeface はダブルクォーテーションで囲んだフォント名を指定します
フォントは WIN.INI の [FONTS] セクションで定義されているものである必要があります

control-statement は、ダイアログに表示するコントロールを指定することができます
ダイアログボックスの内部は、複数のコントロールによって構成されるのです
ここで指定できるステートメントも、少しずつ説明していきます
とりあえず、今はダイアログの表示とその仕組みを理解することに専念しましょう

リソーススクリプトで定義したダイアログボックスを表示するには
DialogBox() 関数を使います(正確に言うと、これはマクロです)
この関数は、モーダルダイアログボックス と呼ばれるダイアログを生成します
モーダルダイアログボックスとは、ダイアログが閉じるまで制御が戻らないダイアログです
int DialogBox(
	HINSTANCE hInstance , LPCTSTR lpTemplate ,
	HWND hWndParent , DLGPROC lpDialogFunc
);
hInstance は、ダイアログのリソースが格納されているモジュールのインスタンス
lpTemplate は、ダイアログの ID を指定します
hWndParent はこのダイアログを所有するウィンドウのハンドルを指定します

さて、問題は DLGPROC 型の引数です。これはなんでしょう
実はこれは、ウィンドウプロシージャのようにコールバック関数へのポインタを指定します
DLGPROC 型の関数は次の様になっています
BOOL CALLBACK DialogProc(
	HWND hwndDlg , UINT uMsg ,
	WPARAM wParam , LPARAM lParam
);
引数は WindowProc() (すなわちウィンドウプロシージャ)と同じです
しかし、戻り値は数値ではなく BOOL 型であることに注意してください
この関数は、メッセージを処理した場合は TRUE、そうでなければ FALSE を返します

DialogBox() 関数の戻り値は、モーダルダイアログボックスを閉じた時に発生します
モーダルダイアログボックスを閉じるには EndDialog() 関数を使います

BOOL EndDialog(HWND hDlg , int nResult);

hDlg は、破棄するダイアログボックスのハンドルを
nResult は、DialogBox() 関数の戻りとを指定します
関数が成功すると 0 以外、失敗すると 0 が返ります
この関数で指定した nResult の値が、DialogBox() 関数の戻り値になります

プログラムは、モーダルダイアログボックスが閉じられるまで制御できません
//この時、他のウィンドウへ制御を移すことは可能です
モーダルダイアログボックスの操作は DialogProc() に送られるメッセージで処理します
/*リソーススクリプト*/
KITTY DIALOG 10 , 10 , 100 , 50
FONT 16 , "MS Shell Dlg" {}
#include <windows.h>

BOOL CALLBACK DialogProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_LBUTTONUP:
		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;
}


灰色の四角が、今回作成したダイアログボックスです
決して、灰色のブラシと黒のペンで描画しているわけではございません

ウィンドウのクライアントエリアをクリックするとダイアログが表示され
ダイアログをクリックすると、ダイアログは破棄されウィンドウに制御が戻ります

Dialog() 関数は、実際にはマクロとして定義されています
Dialog() マクロは、ダイアログテンプレートを情報として CreateWindow() を呼び出しているのです
こうして、私たちはダイアログ生成の大部分をマクロに任せることができるのです


ダイアログオプション

上で、空のダイアログボックスを表示させることができました
しかし、このダイアログボックスはタイトルバーなどが存在しないため
ウィンドウをマウスで動かすなどの基本的な動作がサポートされていません

これらのダイアログのスタイルなどは、DIALOG 文の optional-statements で指定できます
ここに STYLE 文を使うことでダイアログのスタイルを指定できます

STYLE style

style には、演算子 | で複数のウィンドウスタイルを指定することができます
スタイルは、CreateWindow() 関数のスタイルで指定する WS をプリフィックスとする
ウィンドウスタイル定数を指定することができます
そのほかに DS_LOCALEDIT、DS_MODALFRAME、DS_NOIDLEMSG、DS_SYSMODAL を
指定することも可能です。詳しくは CreateWindow() 関数を参照してください

CreateWindowEX() で指定する拡張スタイルは
STYLE 同様の方法で EXSTYLE 文で指定できます

EXSTYLE extended-style

extended-style には WS_EX をプリフィックスとする
各章ウィンドウスタイルを指定することができます

また、タイトルバーの文字は CAPTION 文で指定できます
スタイルで WS_CAPTION が指定されていなくても
このステートメントを指定すれば、自動的にタイトルバーが付加されます

CAPTION "captiontext"

captiontext には、表示する文字列をクォーテーションで囲んで指定します
これで、基本的なウィンドウとしての機能を備えたダイアログを生成できます
/*リソーススクリプト*/
KITTY DIALOG 10 , 10 , 100 , 50
FONT 16 , "MS Shell Dlg"
STYLE WS_CAPTION | WS_BORDER | WS_SYSMENU
CAPTION "Magical nyan nyan TARUTO" {}
#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;
}


今度は、移動や最大化などの動作も可能なダイアログボックスです
システムから WM_CLOSE が送られると制御をウィンドウに返します

ダイアログも、ウィンドウ同様にメニューを持つことができます
ダイアログがメニューを持つには MENU 文を指定します

MENU menuname

menuname には、メニューの名前を指定します
これだけで、ダイアログにメニューを持たせることができるのです
因みに、タイトルバーが無いダイアログでも問題はありません
//リソーススクリプト
TARUTO MENU {
	POPUP "魔法少女猫" {
		MENUITEM "たると" , 40001
		MENUITEM "ちとせ" , 40002
		MENUITEM "シャルロッテ" , 40003
	}
}

KITTY DIALOG 10 , 10 , 100 , 50
FONT 16 , "MS Shell Dlg"
CAPTION "Magical nyan nyan TARUTO"
MENU TARUTO {}


プログラムは、上のプログラムと同じものを使っているとします
リソーススクリプトだけをコンパイルして実行してください


DialogBox()

int DialogBox(
	HINSTANCE hInstance , LPCTSTR lpTemplate ,
	HWND hWndParent , DLGPROC lpDialogFunc
);
モーダルダイアログボックスを生成して表示します
指定されたコールバック関数が EndDialog() を呼び出すまで制御を返しません

hInstance - ダイアログが格納されているモジュールのインスタンスを指定します
lpTemplate - ダイアログテンプレートの ID を指定します
hWndParent - ダイアログを所持するウィンドウを指定します
lpDialogFunc - ダイアログプロシージャのポインタを指定します

戻り値 - EndDialog() の nResult パラメータの値。失敗すると -1

DialogProc()

BOOL CALLBACK DialogProc(
	HWND hwndDlg , UINT uMsg ,
	WPARAM wParam , LPARAM lParam
);
ユーザー定義のコールバック関数です
ダイアログに送られたメッセージを処理します
戻り値は WM_INITDIALOG の場合だけは特別なので、このメッセージも参照してください

hwndDlg - ダイアログボックスのハンドルが格納されています
msg - メッセージが格納されています
wParam - メッセージの追加情報が格納されています
lParam - メッセージの追加情報が格納されています

戻り値 - メッセージを処理した場合は 0 以外、そうでなければ 0

EndDialog()

BOOL EndDialog(HWND hDlg , int nResult);

モーダルダイアログボックスを終了します

hDlg - 破棄するダイアログのウィンドウハンドルを指定します
nResult - DialogBox() 関数の戻り値を指定します

戻り値 - 成功すると 0 以外、失敗すると 0



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