EternalWindows
メニュー / ポップアップメニュー

メニューの項目を選択するとさらにメニューが出るようなとき、 そのメニューのことをポップアップメニューと呼ぶことがあります (サブメニューやドロップダウンメニューと呼ぶこともあります)。 ポップアップメニューは、CreatePopupMenuで作成することになります。

HMENU CreatePopupMenu(VOID);

このメニューも最初は空の状態であるため、 InsertMenuItemで項目を追加することになります。 ポップアップメニューは項目の選択によって表示されるメニューですから、 このメニューをSetMenuでウインドウに割り当てるようことはありません。

今回のプログラムは、ポップアップメニューを持つ項目を追加します。

#include <windows.h>

#define ID_FILE  10
#define ID_HELP  20
#define ID_SAVE  30
#define ID_EXIT  40
#define ID_ABOUT 50

void InitializeMenuItem(HMENU hmenu, LPTSTR lpszItemName, int nId, HMENU hmenuSub);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_CREATE: {
		HMENU hmenu;
		HMENU hmenuPopupFile;
		HMENU hmenuPopupHelp;

		hmenu  = CreateMenu();
		hmenuPopupFile = CreatePopupMenu();
		hmenuPopupHelp = CreatePopupMenu();

		InitializeMenuItem(hmenuPopupFile, TEXT("保存(&S)"), ID_SAVE, NULL);
		InitializeMenuItem(hmenuPopupFile, NULL, 0, NULL);
		InitializeMenuItem(hmenuPopupFile, TEXT("終了(&X)"), ID_EXIT, NULL);
		
		InitializeMenuItem(hmenuPopupHelp, TEXT("バージョン情報(&A)"), ID_ABOUT, NULL);

		InitializeMenuItem(hmenu, TEXT("ファイル(&F)"), ID_FILE, hmenuPopupFile);
		InitializeMenuItem(hmenu, TEXT("ヘルプ(&H)"), ID_HELP, hmenuPopupHelp);
		
		SetMenu(hwnd, hmenu);

		return 0;
	}
	
	case WM_COMMAND: {
		int nId = LOWORD(wParam);

		if (nId == ID_SAVE)
			MessageBox(hwnd, TEXT("保存が選択されました。"), TEXT("OK"), MB_OK);
		else if (nId == ID_EXIT)
			PostMessage(hwnd, WM_CLOSE, 0, 0);
		else if (nId == ID_ABOUT)
			MessageBox(hwnd, TEXT("Menu Sample Ver 1.0"), TEXT("バージョン情報"), MB_OK);
		else
			;
		
		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void InitializeMenuItem(HMENU hmenu, LPTSTR lpszItemName, int nId, HMENU hmenuSub)
{
	MENUITEMINFO mii;
	
	mii.cbSize = sizeof(MENUITEMINFO);
	mii.fMask  = MIIM_ID | MIIM_TYPE;
	mii.wID    = nId;

	if (lpszItemName != NULL) {
		mii.fType      = MFT_STRING;
		mii.dwTypeData = lpszItemName;
	}
	else
		mii.fType = MFT_SEPARATOR;

	if (hmenuSub != NULL) {
		mii.fMask   |= MIIM_SUBMENU;
		mii.hSubMenu = hmenuSub;
	}

	InsertMenuItem(hmenu, nId, FALSE, &mii);
}

前節と同じようにメインメニューは、「ファイル」と「ヘルプ」という項目を持っています。 しかし、今回はどちらの項目もポップアップメニューを持っています。 InsertMenuItemでは、項目の追加の際にその項目がポップアップメニューを持つ場合、 MENUITEMINFO構造体のhSubMenuでそのハンドルを指定します。 よって、メインメニューの項目を追加する前にまず、 ポップアップメニューを作成しなければなりません。

hmenuPopupFile = CreatePopupMenu();

InitializeMenuItem(hmenuPopupFile, TEXT("保存(&S)"), ID_SAVE, NULL);
InitializeMenuItem(hmenuPopupFile, NULL, 0, NULL);
InitializeMenuItem(hmenuPopupFile, TEXT("終了(&X)"), ID_EXIT, NULL);

hmenuPopupFileという名前は、「ファイルという項目のポップアップメニュー」 というように解釈してください。 今回のInitializeMenuItemは、第4引数にポップアップメニューのハンドルを指定できます。 メインメニューの項目の追加では、ここにハンドルを指定します。 2つ目のInitializeMenuItemでは第2引数がNULLとなっています。 InitializeMenuItemは第2引数がNULLの場合、セパレータを追加するよう設計されます。 次のコードは、InitializeMenuItemの一部です。

if (lpszItemName != NULL) {
	mii.fType      = MFT_STRING;
	mii.dwTypeData = lpszItemName;
}
else
	mii.fType = MFT_SEPARATOR;

if (hmenuSub != NULL) {
	mii.fMask   |= MIIM_SUBMENU;
	mii.hSubMenu = hmenuSub;
}

見て分かるように、lpszItemNameがNULLのときはfTypeにMFT_SEPARATORを代入しています。 セパレータは文字列ではないので、dwTypeDataを初期化する必要はありません。 また、hmenuSubがNULLではないときは、hSubMenuを初期化している点も重要です。 hSubMenuメンバを使用しているので、fMaskメンバにはMIIM_SUBMENUを加えます。

hmenu  = CreateMenu();
InitializeMenuItem(hmenu, TEXT("ファイル(&F)"), ID_FILE, hmenuPopupFile);
InitializeMenuItem(hmenu, TEXT("ヘルプ(&H)"), ID_HELP, hmenuPopupHelp);

メインメニューを作成するコードです。 第4引数がNULLでないため、どちらの項目もポップアップメニューを持つことになります。

デフォルトアイテムについて

1つのメニューは、それに含まれる項目の1つをデフォルトアイテムにすることができます。 デフォルトアイテムは、親メニューをダブルクリックしたときに自動的に選択され、 項目の文字列は他の項目より大きく表示されるという特徴があります。 デフォルトアイテムを設定するには、MENUITEMINFO構造体のfMaskにMIIM_STATEを加えてfStateにMFS_DEFAULTを指定する方法と、 SetMenuDefaultItemを呼び出す方法があります。

SetMenuDefaultItem(hmenuPopupFile, ID_EXIT, 0);

今回のプログラムでこの呼び出しを行った場合、ID_EXITはデフォルトアイテムになります。 これにより、ファイルという項目をダブルクリックしたときには、 ID_EXITがデフォルトで選択されることになります。 第2引数に項目の位置を指定する場合は、第3引数に0以外の値を指定します。



戻る