メニューの動的生成

メニューはリソースで定義して読み込むのが一般的ですが、プログラム上でゼロから作ることもできます。
状況に合わせて項目を追加したり削除したりすることも可能です。

メニューの新規作成

CreateMenu関数

プログラムからメニューを作成するにはCreateMenu関数を使用します。

HMENU CreateMenu();
新しいメニューを作成する。
失敗した場合はNULLを返す。

CreatePopupMenu関数

ショートカットメニュー(サブメニュー)をプログラムからメニューを作成するにはCreatePopupMenu関数を使用します。

HMENU CreatePopupMenu();
新しいドロップダウンメニュー、サブメニュー、ショートカットメニューを作成する。
失敗した場合はNULLを返す。

InsertMenuItem関数

CreateMenu関数で作成されたメニューは空の状態です。
項目を追加するにはInsertMenuItem関数を使用します。

BOOL InsertMenuItemW(
 HMENU hmenu,
 UINT item,
 BOOL fByPosition,
 LPCMENUITEMINFOW lpmi
);
メニューhmenuに、新しいメニュー項目lpmiを位置itemに挿入する。
成功した場合は0以外を、失敗した場合は0を返す。

この関数の引数の意味はSetMenuItemInfo関数とほぼ同じです。

hmenuは項目を追加するメニューのハンドルです。

itemは項目を追加する位置で、fByPositonの値によって意味が変わります。

fByPositonFALSE(0)の場合、itemはメニュー項目の識別子を意味します。
TRUE(0以外)の場合は先頭からのインデックス(番号)を意味します。
新しく追加される項目はここで指定した位置に挿入され、指定位置に既存の項目がある場合はひとつ後ろにずらされます。

lpmiiは新しく追加する項目の情報をMENUITEMINFO構造体へのポインタで指定します。

DeleteMenu関数

メニューから項目を削除するにはDeleteMenu関数を使用します。

BOOL DeleteMenu(
 HMENU hMenu,
 UINT uPosition,
 UINT uFlags
);
メニューhMenuから項目uPositionを削除する。
成功した場合は0以外を、失敗した場合は0を返す。

hmenuは項目を削除するメニューのハンドルです。

uPositionは削除する項目の位置ですが、uFlagsによって意味が変わります。

uFlagsは以下の定数のいずれかを指定します。

  • MF_BYCOMMAND - uPositionはメニュー識別子を意味する。
  • MF_BYPOSITION - uPositionは0から始まる項目の位置番号を意味する。

RemoveMenu関数

DeleteMenu関数は、項目がサブメニューを持つ場合にそのサブメニューのハンドルを破棄します。
メニュー項目の削除にはRemoveMenu関数というのもあり、こちらはハンドルを破棄しません。
引数はDeleteMenu関数と同じです。

ハンドルを破棄しないため、削除の前にあらかじめGetSubMenu関数でハンドルを取得してグローバル変数やstatic変数などに格納しておく必要があります。
(取得しておかないと後で破棄する手段が無くなる)

DrawMenuBar関数

ウィンドウに割り当て済みのメニューに項目を追加または削除した場合、DrawMenuBar関数でメニューを更新する必要があります。

BOOL DrawMenuBar(
 HWND hWnd
);
ウィンドウhWndに割り当てられているメニューを再描画する。
成功した場合は0以外を、失敗した場合は0を返す。

この関数はウィンドウにメニューを割り当て済みであれば、そのウィンドウが表示状態か否かにかかわらず呼び出す必要があります。

メニューの情報

GetMenuItemCount関数

メニューの項目数を取得するにはGetMenuItemCount関数を使用します。

int GetMenuItemCount(
 HMENU hMenu
);
メニューhMenuの項目数を取得する。

メニューハンドルにはトップレベルのメニュー(メニューバー)やサブメニューを指定できます。

GetMenuItemID関数

メニュー項目のID(識別子)を取得するにはGetMenuItemID関数を使用します。

UINT GetMenuItemID(
 HMENU hMenu
 int nPos
);
メニューhMenuのnPos番目の識別子を取得する。

メニューの最初の項目は0から始まります。

項目に識別子が割り当てられていない場合、またはサブメニューを開く項目である場合は「-1」を返します。

サンプルコード

メニューの動的生成のサンプルコードです。


//このリソースファイルは使用しない

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

IDR_MENU1 MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        POPUP "保存(&S)"
        BEGIN
            MENUITEM "上書き保存(&S)",                   ID_SAVE
            MENUITEM "名前を付けて保存(&A)",                ID_SAVEAS
        END
        MENUITEM SEPARATOR
        MENUITEM "終了(&Q)",                      ID_QUIT
    END
    POPUP "編集(&E)"
    BEGIN
        MENUITEM "全て選択(&A)",                    ID_SELECTALL
    END
END

このリソーススクリプトと同等のメニューをプログラムから作成します。
このリソースファイルは読み込みません。


#include <windows.h>

//↓使用しない
//#include "resource.h"

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

#define ID_QUIT                         40001
#define ID_SAVE                         40002
#define ID_SAVEAS                       40003
#define ID_SELECTALL                    40004

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HMENU hMenu;
	HMENU hSubMenu1, hSubMenu2;
	MENUITEMINFO mii;

	switch (message)
	{
	case WM_CREATE:
		hMenu = CreateMenu();

		//ファイル↓
		mii.cbSize = sizeof(MENUITEMINFO);
		mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_SUBMENU;
		mii.fType = MFT_STRING;
		mii.dwTypeData = L"ファイル(&F)";
		hSubMenu1 = mii.hSubMenu = CreatePopupMenu();
		InsertMenuItem(hMenu, 0, TRUE, &mii);

		//保存→
		mii.dwTypeData = L"保存(&S)";
		hSubMenu2 = mii.hSubMenu = CreatePopupMenu();
		InsertMenuItem(hSubMenu1, 0, TRUE, &mii);

		//セパレーター
		mii.fMask = MIIM_FTYPE;
		mii.fType = MFT_SEPARATOR;
		InsertMenuItem(hSubMenu1, 1, TRUE, &mii);

		//終了
		mii.fMask = MIIM_TYPE | MIIM_ID;
		mii.fType = MFT_STRING;
		mii.dwTypeData = L"終了(&Q)";
		mii.wID = ID_QUIT;
		InsertMenuItem(hSubMenu1, 2, TRUE, &mii);

		//上書き保存
		mii.dwTypeData = L"上書き保存(&S)";
		mii.wID = ID_SAVE;
		InsertMenuItem(hSubMenu2, 0, TRUE, &mii);

		//名前を付けて保存
		mii.dwTypeData = L"名前を付けて保存(&A)";
		mii.wID = ID_SAVEAS;
		InsertMenuItem(hSubMenu2, 1, TRUE, &mii);

		//編集↓
		mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
		mii.fType = MFT_STRING;
		mii.dwTypeData = L"編集(&E)";
		hSubMenu1 = mii.hSubMenu = CreatePopupMenu();
		InsertMenuItem(hMenu, 1, TRUE, &mii);

		//全て選択
		mii.fMask = MIIM_TYPE | MIIM_ID;
		mii.dwTypeData = L"全て選択(&Q)";
		mii.wID = ID_SELECTALL;
		InsertMenuItem(hSubMenu1, 0, TRUE, &mii);

		SetMenu(hWnd, hMenu);
		break;

	case WM_COMMAND: //コントロールの操作
		//省略
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

メニューの動的生成

メニュー項目を追加するためにMENUITEMINFO構造体を使用します。
MENUITEMINFO構造体はfMaskメンバで設定(および取得)する情報をフィルタリングできるので、フラグに指定していないメンバは初期化せず使いまわしても問題ありません。
全てのメニュー項目を作成したら、最後にSetMenu関数でウィンドウにメニューを割り当てます。