アークピットのホームページに戻る

WinAPIトピックのトップページに戻る

APIトピックの各章に移動する

ダウンロードのページに移動する
ダウンロードができ
ない場合の対処法
 

ページ移動

2-4-10. メニューの管理

 MDIの子ウィンドウ(ドキュメントウィンドウ)にはメニューがありません。よって種類の異なる子ウィンドウごとに何らかのコマンドを実行するために、メインメニューを操作する必要が生じます。

3つのメニュー

 ApMdisys.rc ファイルには3つのメニューがあります。ApMdisys,ApMdiHello,ApMdiRedit の3つです。ApMdisys はMDI子ウィンドウが無い場合かどれもアクティブでない場合のメニューで、「ファイル ウィンドウ」の2つのメニューがあります。ApMdiHello はApHelloがアクティブの場合のメニューで、「ファイル 表示 ウィンドウ」の3つのメニューがあります。ApMdiRedit はApReditがアクティブの場合のメニューで、「ファイル 書式 ウィンドウ」の3つのメニューがあります。

 WinMainで、この3つのメニューをロードして、グローバル変数にハンドルを設定しておきます。そしてフレームウィンドウの作成時には、MDI子ウィンドウはまだ1つもありませんので、hMenuInit のメニューを設定します。

    hMenuInit  = LoadMenu(hInstance,"ApMdisys");
    hMenuHello = LoadMenu(hInstance,"ApMdiHello");
    hMenuRedit = LoadMenu(hInstance,"ApMdiRedit");

MDIクライアントウィンドウを作成時にメニューを指定

 フレームウィンドウのウィンドウプロシージャ(ApMdiProc)の WM_CREATE メッセージ(下記のコード)で、普通はMDIクライアントウィンドウを作成します。 その時に、最初のメニューを指定します。
        case WM_CREATE:       // メインウィンドウの新規作成
        {
            LPCREATESTRUCT     lpcs;
            CLIENTCREATESTRUCT ccs;
            HINSTANCE hInst;

            hInst = (HINSTANCE)GetWindowLong(hWnd,GWL_HINSTANCE);
            lpcs  = (LPCREATESTRUCT)lParam;
            ccs.hWindowMenu  = GetSubMenu(lpcs->hMenu,IDMC_WINDOW);
            ccs.idFirstChild = ID_MDICHILD;
            hWndClient = CreateWindow("MDICLIENT",NULL,
                                WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|
                                WS_HSCROLL|WS_VSCROLL,
                                0,0,0,0,hWnd,lpcs->hMenu,
                                hInst,(LPVOID)&ccs);
 MDIは「ウィンドウ」メニューの最後にオープン中のMDI子ウィンドウを表示し、アクティブを切り替えることができます。よってMDIは、ウィンドウメニューを識別する必要があります。それを行なうのが上記の、CLIENTCREATESTRUCT 構造体の hWindowMenu メンバです。これにウィンドウメニューのポップアップのハンドルを設定します。
 CreateWindow でメインメニューのハンドルを渡します。これはクライアントウィンドウ上にメニューを作成する訳ではありません。MDIのウィンドウメニュー操作するために、メニューのハンドルを管理します。

MDI子ウィンドウのアクティブが切り替わった時の処理

 MDIの子ウィンドウをクリックしたりウィンドウメニューを実行すると、アクティブが移動します。すると、対象のMDI子ウィンドウのプロシージャに、WM_MDIACTIVATE メッセージが発生します。まず非アクティブになるウィンドウに、wParam にウィンドウハンドルを渡して発生します。次にアクティブになるウィンドウに、lParam にウィンドウハンドルを渡して発生します。下記は、ApHelloのMDI子ウィンドウのプロシージャの WM_MDIACTIVATE メッセージの例です。
        case WM_MDIACTIVATE:
        {
            HMENU hMenu;

            if ((HWND)lParam == hWnd)  // アクティブになった
            {
                hMenu = GetSubMenu(hMenuHello,IDMC_WINDOW+1);
                SendMessage(hWndClient,WM_MDISETMENU
                                ,(WPARAM)hMenuHello,(LPARAM)hMenu);
            }
            else                       // 非アクティブになった
            {
                hMenu = GetSubMenu(hMenuInit,IDMC_WINDOW);
                SendMessage(hWndClient,WM_MDISETMENU
                                 ,(WPARAM)hMenuInit,(LPARAM)hMenu);
            }
            DrawMenuBar(hWndMain);
            return 0;
        }
 このメッセージで、メニューをそっくり交換します。最初にWinMainで LoadMenu した3つのメニューのどれかを、WM_MDISETMENU で設定します。ApHelloの場合は、アクティブなら、hMenuHello で、非アクティブなら、hMenuInit を設定します。それと、ウィンドウのサブメニューのハンドルも渡します。hMenuHello の場合は、2番目(IDMC_WINDOW+1)で、hMenuInit の場合は、1番目(IDMC_WINDOW)です。
 WM_MDISETMENU メッセージはMDIクライアントウィンドウに送信します。つまりこれらのメニューを管理しているのはこのウィンドウです。

フレームウィンドウの WM_INITMENUPOPUP メッセージの処理

 キーやマウスで操作することで、メニューがポップアップする時に、フレームウィンドウのプロシージャの、WM_INITMENUPOPUP メッセージが発生します。ここではメニューの有効・無効を設定したり、チェックマークなどを付けたとする処理を行ないます。
        case WM_INITMENUPOPUP:  // メニューがポップアップする前に発生
        {
            HMENU   hMenu;
            HWND    hMdi;
            WNDPROC proc;
            INT     n;

            if ((BOOL)HIWORD(lParam)) return 0;  // コントロールメニュー
            hMdi = (HWND)SendMessage(hWndClient,WM_MDIGETACTIVE,0,0);
            if (!hMdi) return 0;
            hMenu = (HMENU)wParam;
            proc = (WNDPROC)GetWindowLong(hMdi,GWL_WNDPROC);
            switch (GetMenuItemID(hMenu,0))
            {
               case IDM_LEFT: // 表示メニュー
                   if (proc != HelloProc) break;
                   for(n=IDM_LEFT; n <= IDM_RIGHT; ++n)
                      CheckMenuItem(hMenu,n,MF_BYCOMMAND|MF_UNCHECKED);
                   n = GetWindowLong(hMdi,0);
                   CheckMenuItem(hMenu,n,MF_BYCOMMAND|MF_CHECKED);
                   break;
               case IDM_BOLD: // 書式メニュー
               {
                     : (その他のコード)
                   break;
               }
            }
            return 0;
        }
 現在アクティブのMDI子ウィンドウを得るには、MDIクライアントウィンドウに、WM_MDIGETACTIVE メッセージを送信します。そして、GetWindowLong(hMdi,GWL_WNDPROC)でウィンドウプロシージャのアドレスを得て、子ウィンドウの種類を特定します。また、GetMenuItemID(hMenu,0)でポップアップするメニューの先頭のコマンドのIDを調べて、どのメニューか判断します。

 この様な処理にしたのは、メニューの位置がMDI子ウィンドウを最大化した場合と、他の場合で変わるからです。最大化すると、メニューの位置0は、子ウィンドウのコントロールメニューになります。そしてファイルメニューが位置1になります。最大化していないときは、ファイルメニューが位置0になります。

ページ移動