メニューの動的生成
メニューはリソースで定義して読み込むのが一般的ですが、プログラム上でゼロから作ることもできます。
状況に合わせて項目を追加したり削除したりすることも可能です。
メニューの新規作成
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
の値によって意味が変わります。
fByPositon
がFALSE
(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関数でウィンドウにメニューを割り当てます。