MDI子ウィンドウの操作

MDI子ウィンドウのメッセージ

MDI子ウィンドウを操作するために、以下のメッセージが用意されています。
これらのメッセージは全てクライアントウィドウに送信します。

メッセージ WPARAM LPARAM 戻り値
WM_MDIACTIVATE MDI子ウィンドウハンドル 0 0

(送信)
指定のMDI子ウィンドウをアクティブにする。

(受信)
このメッセージはMDI子ウィンドウのアクティブ状態の変更時に、全てのMDI子ウィンドウに送信される。
WPARAMには非アクティブ化されるウィンドウのハンドル
LPARAMにはアクティブ化されるウィンドウのハンドル
が格納されている。

WM_MDINEXT MDI子ウィンドウハンドル 対象のウィンドウを示す値
(説明を参照)
0

指定のMDI子ウィンドウの前または次の子ウィンドウをアクティブにする。

LPARAMが0の場合、WPARAMで指定した子ウィンドウの次のウィンドウをアクティブにする。
0以外の場合、WPARAMで指定した子ウィンドウの前のウィンドウをアクティブにする。

WM_MDIGETACTIVE 0 子ウィンドウの最大化状態を格納するBOOL型のポインタ
(NULLも可)
MDI子ウィンドウハンドル

現在アクティブになっているMDI子ウィンドウのハンドルを取得する。

LPARAMには子ウィンドウの最大化状態を格納するBOOL型変数のポインタを指定する。
(メッセージの送信後に状態を表す値が格納される)
TRUE: ウィンドウは最大化状態
FALSE: ウィンドウは非最大化状態
必要ない場合はNULLを指定できる。

WM_MDICASCADE 0または整列方法を示す定数
(説明を参照)
0 成功した場合はTRUE
失敗した場合はFALSE

MDI子ウィンドウを重ねて整列する。

WPARAMは以下の定数の組み合わせを指定できる。
MDITILE_SKIPDISABLED: 無効な子ウィンドウを無視
MDITILE_ZORDER: Zオーダー順に並べる

WM_MDITILE 整列方法を示す定数
(説明を参照)
0 成功した場合はTRUE
失敗した場合はFALSE

MDI子ウィンドウを並べて整列する。

WPARAMは以下の定数の組み合わせを指定する。
MDITILE_HORIZONTAL: 子ウィンドウを水平に整列
MDITILE_VERTICAL: 子ウィンドウを垂直に整列
MDITILE_SKIPDISABLED: 無効な子ウィンドウを無視

WM_MDIICONARRANGE 0 0 MDI子ウィンドウハンドル

最小化されている全てのMDI子ウィンドウを整列する。

WM_MDIMAXIMIZE MDI子ウィンドウハンドル 0 0

指定のMDI子ウィンドウを最大化する。

WM_MDIRESTORE MDI子ウィンドウハンドル 0 0

指定のMDI子ウィンドウの最大化/最小化状態を元に戻す。

WM_MDICREATE 0 MDICREATESTRUCT構造体へのポインタ 成功した場合は作成した子ウィンドウのハンドル
失敗した場合はNULL

MDI子ウィンドウを作成する。
(MDIアプリ#WM_MDICREATEメッセージを参照)

WM_MDIDESTROY MDI子ウィンドウのハンドル 0 0

MDI子ウィンドウを破棄する。

WM_MDISETMENU メニューハンドル
(フレームウィンドウ)
メニューハンドル
(ウィンドウメニュー)
古いフレームウィンドウのメニューハンドル
失敗した場合は0

フレームウィンドウのメニューをWPARAMで指定したメニューに置き換える。
ウィンドウメニューLPARAMで指定したメニューに設定する。
どちらもNULLを指定することができ、その場合は置き換えは起こらない。
このメッセージの送信後、フレームウィンドウに対してDrawMenuBar関数を実行してメニューを更新する必要がある。

※ここで言うウィンドウメニューとは、MDI子ウィンドウの一覧が表示されるサブメニューのこと。
(旧称「システムメニュー」のことではない)

WM_MDIREFRESHMENU 0 0 フレームウィンドウのメニューハンドル
失敗した場合は0

フレームウィンドウのウィンドウメニューを更新する。
このメッセージの送信後、フレームウィンドウに対してDrawMenuBar関数を実行してメニューを更新する必要がある。

子ウィンドウ操作の例

上記のメッセージを利用した、子ウィンドウの操作例です。
メニューの各項目の表示の通りの操作が可能です。


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "終了(&X)",                      ID_QUIT
    END
    POPUP "ウィンドウ(&W)"
    BEGIN
        MENUITEM "新規作成(&N)",                    ID_CREATECHILD
        MENUITEM SEPARATOR
        POPUP "整列"
        BEGIN
            MENUITEM "重ねる(&C)",                     ID_MDICASCADE
            MENUITEM "水平に並べる(&H)",                  ID_MDITILEH
            MENUITEM "垂直に並べる(&V)",                  ID_MDITILEV
        END
        MENUITEM "次のウィンドウ(&F)",                 ID_MDINEXT
        MENUITEM "前のウィンドウ(&B)",                 ID_MDIPREV
        MENUITEM "最大化/元に戻す(&M)",                ID_MDIMAXIMIZE
        MENUITEM "閉じる(&C)",                     ID_MDIDESTROY
        MENUITEM "全て閉じる(&A)",                   ID_MDICLOSEALL
    END
END

MDI子ウィンドウを操作するためのメニュー


#include <windows.h>
#include "resource.h"

#define ID_MDICHILDFIRST 200

//グローバル変数
HWND hClientWnd;

//関数プロトタイプ宣言
//ウィンドウプロシージャ
LRESULT CALLBACK FrameWndProc(HWND, UINT, WPARAM, LPARAM);
//MDI子ウィンドウプロシージャ
LRESULT CALLBACK DocProc(HWND, UINT, WPARAM, LPARAM);
//子ウィンドウの列挙
BOOL CALLBACK EnumChildProc(HWND, LPARAM);

//ウィンドウの生成等は省略

//フレームウィンドウプロシージャ
LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	CLIENTCREATESTRUCT ccs;
	HWND h;
	BOOL b;

	switch (message)
	{
	case WM_CREATE:
		//クライアントウィンドウの作成
		ccs.hWindowMenu = GetSubMenu(GetMenu(hWnd), 1);
		ccs.idFirstChild = ID_MDICHILDFIRST;
		hClientWnd = CreateWindow(L"MDICLIENT", NULL,
			WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL,
			0, 0, 0, 0, hWnd, (HMENU)1, hInst, &ccs);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case ID_QUIT: //終了
			SendMessage(hWnd, WM_CLOSE, 0, 0);
			return 0;

		case ID_CREATECHILD: //子ウィンドウ作成
			h = CreateMDIWindow(szWindowClass_MDIChild, L"MDI子ウィンドウ", 0,
				CW_USEDEFAULT, CW_USEDEFAULT,
				CW_USEDEFAULT, CW_USEDEFAULT,
				hClientWnd, hInst, NULL);
			return 0;

		case ID_MDICASCADE: //重ねて整列
			SendMessage(hClientWnd, WM_MDICASCADE, 0, 0);
			return 0;

		case ID_MDITILEH: //水平に整列
			SendMessage(hClientWnd, WM_MDITILE, MDITILE_HORIZONTAL, 0);
			return 0;

		case ID_MDITILEV: //垂直に整列
			SendMessage(hClientWnd, WM_MDITILE, MDITILE_VERTICAL, 0);
			return 0;

		case ID_MDINEXT: //次のウィンドウ
			h = (HWND)SendMessage(hClientWnd, WM_MDIGETACTIVE, 0, 0);
			if (h)
			{
				SendMessage(hClientWnd, WM_MDINEXT, (WPARAM)h, 0);
			}
			return 0;

		case ID_MDIPREV: //前のウィンドウ
			h = (HWND)SendMessage(hClientWnd, WM_MDIGETACTIVE, 0, 0);
			if (h)
			{
				SendMessage(hClientWnd, WM_MDINEXT, (WPARAM)h, 1);
			}
			return 0;

		case ID_MDIMAXIMIZE: //最大化/元に戻す
			h = (HWND)SendMessage(hClientWnd, WM_MDIGETACTIVE, 0, &b);
			if (h)
			{
				SendMessage(hClientWnd, b ? WM_MDIRESTORE : WM_MDIMAXIMIZE, (WPARAM)h, 0);
			}
			return 0;

		case ID_MDIDESTROY: //閉じる
			h = (HWND)SendMessage(hClientWnd, WM_MDIGETACTIVE, 0, 0);
			if (h)
			{
				SendMessage(hClientWnd, WM_MDIDESTROY, (WPARAM)h, 0);
			}
			return 0;

		case ID_MDICLOSEALL: //全て閉じる
			EnumChildWindows(hClientWnd, EnumChildProc, ID_MDICLOSEALL);
			return 0;
		}
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefFrameProc(hWnd, hClientWnd, message, wParam, lParam);
}

//子ウィンドウプロシージャ
LRESULT CALLBACK DocProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	//省略
}

//子ウィンドウの列挙
BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam) {
	switch (lParam)
	{
	case ID_MDICLOSEALL: //全て閉じる
		SendMessage(hClientWnd, WM_MDIDESTROY, (LPARAM)hWnd, 0);
		break;
	}
	
	return TRUE;
}

全ての子ウィンドウを閉じる

MDI子ウィンドウメッセージには「全ての子ウィンドウを閉じる」というものはありません。
しかしそういった処理をしたい場合もあると思いますので、上記サンプルコードにはその実装例を示しています。


//該当箇所のみ抜粋

LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case ID_MDICLOSEALL: //全て閉じる
			EnumChildWindows(hClientWnd, EnumChildProc, ID_MDICLOSEALL);
			return 0;
		}
	}
	return DefFrameProc(hWnd, hClientWnd, message, wParam, lParam);
}

BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam) {
	switch (lParam)
	{
	case ID_MDICLOSEALL: //全て閉じる
		SendMessage(hClientWnd, WM_MDIDESTROY, (LPARAM)hWnd, 0);
		break;
	}
	
	return TRUE;
}

EnumChildWindows関数は、全ての子ウィンドウに対して一度ずつ処理を行います。

BOOL EnumChildWindows(
 HWND hWndParent,
 WNDENUMPROC lpEnumFunc,
 LPARAM lParam
);
ウィンドウhWndParentの全ての子ウィンドウに対して関数lpEnumFuncを実行する。
追加の情報をlParamに格納できる。
戻り値は使用しない。

第一引数hWndParentは親ウィンドウハンドルです。
クライアントウィンドウのハンドルを指定すると、全てのMDI子ウィンドウが対象になります。

第二引数lpEnumFuncは適用する処理のコールバック関数へのポインタです。

第三引数lParamはコールバック関数に渡す追加の情報です。

第二引数に指定するコールバック関数は以下の形で定義します。

BOOL CALLBACK EnumChildProc(
 HWND hwnd,
 LPARAM lParam
);
EnumChildWindows関数で使用されるコールバック関数の関数定義。

第一引数hwndには操作対象の子ウィンドウハンドルが格納されます。
第二引数lParamはEnumChildWindows関数の第三引数に指定した値が格納されます。

上記コードのコールバック関数では、lParamが「ID_MDICLOSEALL」ならば、クライアントウィンドウにWM_MDIDESTROYメッセージを送信して自分自身(子ウィンドウ)を破棄しています。
全ての子ウィンドウに対して同様の処理が順に実行されます。
(今回は破棄以外の動作はしないのでlParamをチェックする処理は無くても同じです)

このコールバック関数はTRUEを返すと処理を続行します。
(次のウィンドウの処理を開始する)
FALSEを返すと処理を中断します。
今回は常にTRUEを返すので、全ての子ウィンドウの列挙が終わるまで処理は継続されます。