ダイアログ
ウィンドウアプリはメインとなるウィンドウを持つのが基本ですが、必要に応じて複数のウィンドウを表示することもできます。
(→複数のウィンドウを持つアプリ)
しかし、ちょっとした情報の表示や設定ウィンドウとして使うだけならばわざわざもう一枚分のウィンドウを定義するのは面倒です。
メッセージボックスで物足りない場合は自前のダイアログ(ダイアログボックス)を作成できます。
ダイアログの作成
ダイアログはリソースファイルを使用して作成します。
まずはリソースファイルの項を参考にしてリソースファイルを追加します。
次にリソースの追加ダイアログで「Dialog」を選択して新規作成します。
Visual Studioではダイアログをグラフィカルな編集画面で作成することができます。
左上の「ツールボックス」内にコントロール類の一覧があり、ここからコントロールを選択してダイアログ上をクリック(またはドラッグ&ドロップ)することでダイアログ上に配置することができます。
今回は「Static Text」を配置します。
これはスタティックコントロールです。
配置したコントロールは右クリック→「プロパティ」(またはコントロールを選択状態で「F4」キー)から詳細な設定を行うことができます。
(ダイアログウィンドウ自体もプロパティでいろいろ設定できます)
プロパティペインではコントロールのID(識別子)や見た目などを変更できます。
スタティックコントロールなどのテキスト情報は「キャプション」から変更できます。
さらに今回は「テキストの配置」を「Center」に変更してテキストを中央揃えにしてみます。
(下の画像は配置したスタティックコントロールの横幅をウィンドウ一杯に広げています)
プロパティペインはよく使用するので、右上のピン止めアイコンをオンにして常に表示しておくと便利かもしれません。
また、最初から配置されている「OK」と「キャンセル」ボタンのIDも確認しておきましょう。
それぞれ「IDOK」と「IDCANCEL」になっていると思います。
これは後に使用します。
これらのボタンは必要が無ければ削除しても構いません。
コントロールの削除はコントロールを選択して「Del」キーです。
グリッド表示
コントロールを配置する場合、グリッド表示にすると各コントロールを綺麗に整列しやすくなります。
(このアイコンが表示されていない場合は「表示」メニュー→「ツールバー」→「ダイアログエディター」をオンにすると表示されます)
グリッド表示はコントロールの配置やサイズの変更時に一定間隔でスナップするようになります。
要するに位置やサイズなどの調整が簡単になります。
ダイアログのテスト
ダイアログはプログラムを実際に実行する前にその表示をテストすることができます。
上記アイコンをクリック(または「Ctrl + T」)すると、プログラムから呼び出した時と同じ見た目でダイアログが表示されます。
今回はウィンドウサイズとコントロールを以下のように調整しました。
リソースファイルは以下のようになります。
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ で生成されたインクルード ファイル。
// Resource.rc で使用
//
#define IDD_DIALOG1 101
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_DIALOG1 DIALOGEX 0, 0, 200, 70
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,90,48,50,14
PUSHBUTTON "キャンセル",IDCANCEL,144,48,50,14
CTEXT "ダイアログボックスのテストです",IDC_STATIC,6,12,185,8
END
ダイアログの呼び出し
DialogBoxマクロ
プログラムからダイアログボックスを呼び出すにはDialogBox
マクロを使用します。
- INT_PTR DialogBoxW(
HINSTANCE hInstance,
LPCWSTR lpTemplate,
HWND hWndParent,
DLGPROC lpDialogFunc
); - モジュールhInstance内のダイアログボックステンプレートlpTemplateからモーダルダイアログボックスを作成し、オーナーをウィンドウhWndParentとする。
戻り値はEndDialog関数で指定された値。
hWndParentが無効なハンドルな場合は0を返す。
それ以外の理由で関数が失敗した場合は-1を返す。
これは関数ではなくDialogBoxParam関数を呼び出すマクロとして定義されています。
第一引数hInstance
はダイアログテンプレート(次の引数)が含まれる実行ファイルのインスタンスを指定します。
NULL
を指定すると現在の実行ファイルのインスタンスを指定したことになります。
第二引数lpTemplate
はダイアログテンプレートというものですが、これは先ほど作成したダイアログリソースの識別子をMAKEINTRESOURCE
マクロを使用して指定します。
MAKEINTRESOURCEマクロを使用する理由についてはリソースを指定する識別子および文字列についてを参照してください。
第三引数hWndParent
はダイアログボックスの所有者となるウィンドウです。
このダイアログボックスはメッセージボックスと同じくモーダルダイアログボックスで、ダイアログを閉じるまで所有者ウィンドウに制御を返しません。
(処理が一時的に停止する)
第四引数lpDialogFunc
はダイアログプロシージャです。
ダイアログボックスはウィンドウと同じように、メッセージを処理する専用のコールバック関数を持ちます。
その関数をここで指定します。
戻り値は後述するEndDialog
関数で決定します。
ダイアログプロシージャ
ダイアログプロシージャの定義はウィンドウプロシージャとよく似ています。
- INT_PTR CALLBACK DlgProc(
HWND,
UINT,
WPARAM,
LPARAM
){} - ダイアログプロシージャの関数定義。
戻り値の型が異なる以外はウィンドウプロシージャと同じです。
関数名も自由です。
ダイアログプロシージャは、ダイアログ上に配置したコントロールから送られてきたメッセージを処理します。
基本的にはウィンドウプロシージャと同じで、例えばボタンなどのクリックはWM_COMMAND
メッセージが送信され、WPARAMの下位ワードにボタンID(識別子)が格納されています。
ウィンドウプロシージャと異なるのは戻り値の指定です。
ウィンドウプロシージャの場合はメッセージを処理した場合は0を返し、処理しない場合はDefWindowProc関数を実行して返す、という決まりでした。
ダイアログプロシージャの場合は、メッセージを処理した場合はTRUE
を返し、それ以外の場合はFALSE
を返す決まりになっています。
FALSEを返すと、システムがダイアログのデフォルト動作を実行します。
DefWindowProc関数は実行しないでください。
戻り値はINT_PTR型ですが、BOOL型で定義しても問題ありません。
EndDialog関数
もう一つ、EndDialog
関数も使用します。
これは名前の通りダイアログボックスを終了させます。
- BOOL EndDialog(
HWND hDlg,
INT_PTR nResult
); - ダイアログhDlgを戻り値nResultで終了する。
成功した場合は0以外を、失敗した場合は0を返す。
これはダイアログプロシージャ内で使用する関数です。
ダイアログプロシージャの第一引数HWNDにはダイアログのハンドルが格納されています。
これをそのままEndDialog関数の第一引数hDlg
に渡すことでダイアログを終了できます。
第二引数nResult
は、DialogBoxマクロの戻り値となる値です。
(この関数の戻り値ではありません)
例えばダイアログボックスを閉じる際にクリックされたボタンの種類などを格納できます。
サンプルコード
具体的なサンプルコードを示します。
#include <windows.h>
#include "resource.h"
//ウィンドウプロシージャのプロトタイプ宣言
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static WCHAR *txt;
HDC hdc;
PAINTSTRUCT ps;
int ret;
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
CreateWindow(
L"BUTTON", L"Open Dialog",
WS_CHILD | WS_VISIBLE,
10, 10, 100, 24,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
//ダイアログボックスの呼び出し
ret = DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc);
if (ret == IDOK)
txt = L"IDOK";
else
txt = L"IDCANCEL";
InvalidateRect(hWnd, NULL, TRUE);
break;
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 10, 50, txt, lstrlen(txt));
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
//ダイアログプロシージャ
INT_PTR CALLBACK DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDOK:
EndDialog(hWnd, IDOK);
return TRUE;
case IDCANCEL:
EndDialog(hWnd, IDCANCEL);
return TRUE;
}
break;
}
return FALSE;
}
「Open Dialog」ボタンをクリックするとダイアログボックスが表示されます。
「OK」「キャンセル」ボタンをクリックするとダイアログボックスが閉じられ、メインウィンドウにはクリックしたボタンの情報が表示されます。
ちなみに右上の×ボタンをクリックした場合も「IDCANCEL」が送られてきます。