モーダルダイアログボックス

モーダルダイアログとモードレスダイアログボックス

ここでは、重要なリソースである、ダイアログボックスについて説明します。ダイアログボックスのダイアログとは、対話という意味である。その名のとおりダイアログボックスは、コンピュータと情報のやり取りをする場合に使用されるウインドウのことを指します。

ダイアログボックスは大きく分けると、モーダルダイアログボックスモードレスダイアログボックスに分類されます。モーダルダイアログボックスは、ダイアログボックスを閉じるまでは同じアプリケーションの他のウインドウにフォーカスを移動できません。それに対し、モードレスダイアログボックスは、ダイアログボックスを閉じなくても、他のウインドウにフォーカスを移動できます。

ここではまず、手始めにモーダルダイアログボックスの使い方について説明します。

サンプルプログラム

まずは、以下のサンプルを入力し、指示に従って、新しいリソースを追加してから実行してみてください。

win32exproj4-1:WinMain.cpp
// ************************************
// Ex モーダルダイアログ
// ************************************
//必要なヘッダーファイルのインクルード
#define STRICT
 
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
 
//  リソースの読み込み
#include "resource.h"
 
// シンボル定義及びマクロ
#define WINDOW_WIDTH    800
#define WINDOW_HEIGHT   600
 
//  インスタンス(グローバル変数)
HINSTANCE hInst;
 
//  ウィンドウプロシージャのコールバック関数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK DlgWndProc(HWND, UINT, WPARAM, LPARAM);
 
int WINAPI WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
{
    static TCHAR szWindowClass[] = _T("Sample04");
    static TCHAR szTitle[] = _T("モーダルダイアログのサンプル");
 
    WNDCLASSEX wcex;
 
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
 
    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL,
            _T("RegisterClassExの処理に失敗しました"),
            _T("Sample03"),
            NULL);
 
        return 1;
    }
 
    hInst = hInstance; // グローバル変数に値を入れる
 
    // The parameters to CreateWindow explained:
    // szWindowClass                : アプリケーションの名前
    // szTitle                      : タイトルバーに現れる文字列
    // WS_OVERLAPPEDWINDOW          : 生成するウィンドウのタイプ
    // CW_USEDEFAULT, CW_USEDEFAULT : 最初に置くポジション (x, y)
    // WINDOW_WIDTH, WINDOW_HEIGHT  : 最初のサイズ (幅, 高さ)
    // NULL                         : このウィンドウの親ウィンドウのハンドル
    // NULL                         : メニューバー(このサンプルでは使用せず)
    // hInstance                    : WinMain関数の最初のパラメータ
    // NULL                         : WM_CREATE情報(このアプリケーションでは使用せず)
    HWND hWnd = CreateWindow(
        szWindowClass,
        szTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        WINDOW_WIDTH, WINDOW_HEIGHT,
        NULL,
        NULL,
        hInstance,
        NULL
        );
    //  ウィンドウが生成できなかった場合
    if (!hWnd)
    {
        MessageBox(NULL,
            _T("ウィンドウ生成に失敗しました!"),
            _T("Sample04"),
            NULL);
        return 1;
    }
 
    // ウィンドウの表示に必要なパラメータ:
    // hWnd     : CreateWindowの戻り値
    // nCmdShow : WinMainの引数の4番目
    ShowWindow(hWnd,
        nCmdShow);
    UpdateWindow(hWnd);
 
    // メインのメッセージループ:
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}
 
//  ウィンドウプロシージャ(メッセージに対するコールバック関数)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //ポイント構造体
    POINT    pt;
    switch (message)
    {
    //キーを押した
    case WM_KEYDOWN:
        switch(wParam)
        {
        case VK_ESCAPE:
            //終了メッセージを発生させる
            PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
        }
        break;
    //ウインドウが生成されたときに1度だけ通過
    case WM_CREATE:
        break;
    //マウス右クリック
    case WM_RBUTTONDOWN:
        pt.x = LOWORD(lParam);
        pt.y = HIWORD(lParam);
        //クライアント座標をスクリーン座標へ変換
        ClientToScreen( hWnd, &pt);
        //ポップアップメニューを表示
        TrackPopupMenu( GetSubMenu( GetMenu( hWnd ), 0), TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);
        break;
    case WM_COMMAND:
        switch( LOWORD(wParam) )
        {
        case ID_40001:
            //バージョン(A)
            MessageBox(hWnd, _T("メニューの実装Ver1.0"), _T("バージョン情報"), MB_OK);
            break;
        case ID_40002:
            //終了(X)
            PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
        case ID_40003:
            //モーダルダイアログボックスを作成
            DialogBox(
              (HINSTANCE)GetWindowLong( hWnd, GWL_HINSTANCE),
              MAKEINTRESOURCE( IDD_DIALOG1 ),
              hWnd,
              (DLGPROC)DlgWndProc);
            break;
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }
 
    return 0;
}

//--------------------------------------------
// Name:DlgWndProc()
// Desc:ダイアログ用ウィンドウプロシージャ
//--------------------------------------------
BOOL CALLBACK DlgWndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
{
    switch( iMsg )
    {
    case WM_INITDIALOG:
        //ダイアログボックスが生成されたとき
        return TRUE;
    case WM_COMMAND:
        switch( LOWORD(wParam) )
        {
        case IDCANCEL:
            //モーダルダイアログボックスを破棄
            EndDialog(hWnd, 0);
            break;
        }
        return TRUE;
    default:
        return FALSE;
    }
}

リソースの作成

リソースは、メニュー、IDR_MENU1を追加し、メニューの項目を以下のように入力します。(表4-1./図4-1.)

表4-1.IDR_MENU1のメニュー項目のIDとキャプションの組み合わせ
ID キャプション
ID_40001 バージョン(&A)
ID_40002 終了(&X)
ID_40003 ダイアログ(&D)

図4-1.メニューリソースの中身

メニューリソースの中身

続いて、ダイアログリソースを追加します。他のリソースの時と同様、「リソースファイル」を右クリックし、[追加]→[リソース]を選択すると、以下のダイアログが出現するので、Dialogを選択してください。(図4-2.)

図4-2.ダイアログメニューの追加

ダイアログメニューの追加

すると、以下のように新規にダイアログが追加されます。(図4-3.)IDは、IDD_DIALOG1となっています。(図4-4.)

図4-3.追加されたダイアログリソース

追加されたダイアログリソース

リソースの設定が終われば、プログラムの実行が可能です。プログラムを実行すると、メニューのあるウィンドウが起動します。(図4-4.)

図4-4.追加されたリソースのID

追加されたリソースのID果

以上で、リソースの追加は終了です。次は、実際にプログラムを動かしてみます。

プログラムの実行

メニューから「ファイル」を選ぶと、「ダイアログ(D)」という項目があるので、これを選択します。(図4-5.)

図4-5.「ダイアログ」メニューの選択

「ダイアログ」メニューの選択択

すると、ダイアログウィンドウが出現します。(図4-6.)

図4-6.ダイアログウィンドウの出現

ダイアログウィンドウの出現

ダイアログウィンドウが出現している間は、メインとなるウィンドウの操作はできません。ファイルダイアログの「キャンセル」ボタンを押すと、ダイアログウィンドウは消えます。すると、再びメインのウィンドウを操作することが可能になります。

プログラムの仕組み

ダイアログのプロシージャ

今回使用する、ダイアログボックスはウインドウと同じく、対応するウインドウのイベントを処理するプロシージャを用意してあげる必要があります。それが、24行目で宣言されているDlgWndProcプロシージャです。

ダイアログプロシージャ
BOOL CALLBACK DlgWndProc(HWND, UINT, WPARAM, LPARAM);

ダイアログの生成と消去

続いて、モーダルダイアログの生成と消去について説明します。モーダルダイアログは、DialogBox関数で出現させ、EndDialog関数で終了させます。各々の関数の仕様は、以下の通りです。

Dialogbox関数
INT_PTR DialogBox(
    HINSTANCE hInstance,  // モジュールのハンドル
    LPCTSTR lpTemplate,    // ダイアログボックステンプレート
    HWND hWndParent,      // オーナーウィンドウのハンドル
    DLGPROC lpDialogFunc  // ダイアログボックスプロシージャ
);

resource.hで設定してあるアイコン識別子を設定してあげることで、作成したダイアログボックスを使用できます。今回の識別子はIDD_DIALOG1で、プロシージャがDlgWndProcなので、149行目から158目のように実装します。ID_40003は、メニューの項目「ダイアログ(D)」に対応しているため、メニューでこの項目が選択されると、この箇所が実行され、モーダルダイアログが出現します。

Dialogbox関数の実装
case ID_40003:
    //モーダルダイアログボックスを作成
    DialogBox(
      (HINSTANCE)GetWindowLong( hWnd, GWL_HINSTANCE),
      MAKEINTRESOURCE( IDD_DIALOG1 ),
      hWnd,
      (DLGPROC)DlgWndProc);
      break;
}

尚、モジュールのハンドル(インスタンスハンドル)はGetWindowLong関数 を使用することで、ウインドウハンドルから取得しています。

EndDialog関数
BOOL EndDialog(
  HWND hDlg,        // ダイアログボックスのハンドル
  INT_PTR nResult   // 返したい値
);

この関数を呼び出しているのは、DlgWndProc関数の中です。(181行目~189行目)ダイアログの「キャンセル」ボタンを押すと、WM_COMMANDメッセージが発生し、コマンドの種類がIDCANCELであることから、ここにそのイベント処理を記述します。

Dialogbox関数の実装
case WM_COMMAND:
    switch( LOWORD(wParam) )
    {
        case IDCANCEL:
            //モーダルダイアログボックスを破棄
            EndDialog(hWnd, 0);
            break;
     }
     return TRUE;

これにより、ダイアログの「キャンセル」ボタンを押すと、ダイアログが終了します。