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

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

応用編4日目で説明した、モーダルダイアログボックスに続いて、モードレスダイアログボックスについて説明します。モーダルダイアログが、出現と同時にフォーカスが移行し、そのダイアログの親ダイアログを操作できなくなるのに対し、モードレスダイアログは、ダイアログボックスが出現しても、親ウィンドウに対する操作が可能です。

サンプルプログラム

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

win32exproj5-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("Sample05");
    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("Sample05"),
            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)
{
	HWND     hDialog;
    //ポイント構造体
    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:
            //モーダルダイアログボックスを作成
			hDialog = CreateDialog(
				(HINSTANCE)GetWindowLong( hWnd, GWL_HINSTANCE),
				MAKEINTRESOURCE( IDD_DIALOG1 ),
				hWnd,
				(DLGPROC)DlgWndProc);
				//ダイアログボックスを表示
			ShowWindow( hDialog, SW_SHOW);
            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:
           //モードレスダイアログボックスを破棄
            DestroyWindow(hWnd);
            break;
        }
        return TRUE;
    default:
        return FALSE;
    }
}

リソースの作成

リソースは、モードレスダイアログの場合とまったく同じものを用います。メニュー、IDR_MENU1を追加し、メニューの項目を以下のように入力します。(表5-1./図5-1.)

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

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

メニューリソースの中身

ダイアログリソースも、全く同じものを追加してください。(図5-2.図5-3.)

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

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

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

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

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

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

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

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

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

プログラムの実行

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

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

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

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

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

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

モーダルダイアログと違い、ダイアログウィンドウが出現している間もメインとなるウィンドウを操作することは可能です。

プログラムの仕組み

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

モードレスダイアログの場合と同様、ダイアログボックスにプロシージャを用意する必要があります。それが、24行目で宣言されているDlgWndProcプロシージャです。

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

ダイアログの生成と消去

モーダルダイアログとの最大の違いは、ダイアログの生成と消去の方法です。モーダルダイアログは、CreateDialog関数と、ShowWindow関数で出現させ、DestroyWindow関数で終了させます。CreateDialogの関数の仕様は、以下の通りです。

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

この関数により、与えられたパラメータのリソースを持ったダイアログボックスが、ウィンドウとして生成されます。この関数の戻り値が、そのウィンドウへのハンドルとなります。これを実装してあるのが、このサンプルの150行目から159行目です。

モードレスダイアログボックスの生成
case ID_40003:
   //モードレスダイアログボックスを作成
   hDialog = CreateDialog(
      (HINSTANCE)GetWindowLong( hWnd, GWL_HINSTANCE),
      MAKEINTRESOURCE( IDD_DIALOG1 ),
      hWnd,
      (DLGPROC)DlgWndProc);
      //ダイアログボックスを表示
      ShowWindow( hDialog, SW_SHOW);
   break;
}

ShowWindow関数は、与えられたハンドルのウィンドウを出現させる関数です。これに引数として、生成されたダイアログのハンドルhDialogを渡すと、モードレスダイアログが出現します。

最終的に、このダイアログボックスを閉じるのが、DestroyWindow関数です。モーダルダイアログの場合と同様、ダイアロブのプロシージャ内で、「キャンセル」ボタンが押されたときに実行されます。(184行目~192行目)

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

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