メニュー操作


MENUITEMINFO構造体

メニューにはチェック状態やグレイ状態などがありました
当然、これらの状態はプログラムから変更することができます

メニュー操作ファンクションの多くは、メニューのハンドルを必要とします
LoadMenu() 関数でメニューを取得した時のハンドルでもかまいませんが
あらかじめウィンドウに設定されている場合は GetMenu() 関数が便利でしょう

HMENU GetMenu(HWND hWnd);

hWnd には、メニューを持つウィンドウのハンドルを指定します
戻り値はウィンドウが持つメニューのハンドルですが
ウィンドウがメニューを持たない場合は NULL が返ります
(もし子ウィンドウだった場合の戻り値は未定義)

メニューの状態は MENUITEMINFO 構造体によって表現されます
この構造体は、次にように定義されています
typedef struct tagMENUITEMINFO {
    UINT    cbSize; 
    UINT    fMask; 
    UINT    fType; 
    UINT    fState; 
    UINT    wID; 
    HMENU   hSubMenu; 
    HBITMAP hbmpChecked; 
    HBITMAP hbmpUnchecked; 
    DWORD   dwItemData; 
    LPTSTR  dwTypeData; 
    UINT    cch; 
} MENUITEMINFO, FAR *LPMENUITEMINFO;
cbSize は、この構造体のサイズをバイト単位で指定します

fMask は、この構造体を使って設定したり値を取得する関数に対して
どのメンバを設定または取得するかを指示するための情報です
これは、以下の定数を組み合わせて指定します

定数取得、またはセットするメンバ
MIIM_CHECKMARKS hbmpChecked と hbmpUnchecked
MIIM_DATA dwItemData
MIIM_ID wID
MIIM_STATE fState
MIIM_SUBMENU hSubMenu
MIIM_TYPE fType と dwTypeData

fType には、メニュー項目の種類を以下の定数から指定します

定数解説
MFT_BITMAP ビットマップのメニューを表示
dwTypeDataメンバーの下位ワードにビットマップのハンドルを指定します
cchは無視されます
MFT_MENUBARBREAK メニューバーの新しいラインにメニュー項目を配置
MFT_MENUBREAK メニューの新しいラインに項目を配置
MFT_OWNERDRAW オーナー描画メニュー
描画責任がメニューを所持するウィンドウに割り当てられる
MFT_RADIOCHECK チェックマークではなく、ラジオボタンマークを使って
メニュー項目をチェックする
MFT_RIGHTJUSTIFY 右揃えする
メニュー項目がメニューバーにある時のみ有効
MFT_RIGHTORDER Windows95 , NT5.0 以降
アラビア語など、右から左に読む言語をサポートする
MFT_SEPARATOR セパレータであることを表す
MFT_STRING 文字列のメニューであることを表す
dwTypeDataメンバーはヌルで終わる文字列へのポインタ
cchメンバーは文字列の長さである

fState は、メニュー項目の状態を表す定数を組み合わせて指定します
定数は、次のものを指定できます

定数解説
MFS_CHECKED 項目をチェックする
MFS_DEFAULT 項目はデフォルトである
MFS_DISABLED 項目を無効状態にする
MFS_ENABLED 項目を有効状態にする(デフォルト)
MFS_GRAYED 項目をグレー状態にする
MFS_HILITE 項目をハイライト状態にする
MFS_UNCHECKED 項目のチェックを外す
MFS_UNHILITE 項目のハイライトを削除する

wID は、項目の識別用IDを指定します
この値は、アプリケーション定義の16ビット値になります

hSubMenu はメニューに結びついたドロップダウンメニュー
またはサブメニューのハンドルを格納します
メニューがドロップダウン、またはサブメニューを持たないならば NULL です

hbmpUnchecked はビットマップのハンドルを
dwItemData は、項目に結び付けられたアプリケーション定義の値
dwTypeData はヌル文字で終わる文字列へのポインタを指定
cch はテキストメニューの文字列の長さを格納します

メニューの状態を変更するには、この構造体を宣言し
変更部分を fMask メンバに設定し、必要なメンバをセットします
そして、SetMenuItemInfo() 関数にこの構造体のポインタを渡します
BOOL SetMenuItemInfo(
	HMENU hMenu , UINT uItem ,
	BOOL fByPosition , LPMENUITEMINFO lpmii 
);
hMenu には、変更するメニューのハンドルを指定します
uItem はメニューアイテムの ID またはインデックスを指定します
この判断は fByPosition で指定することができます

fByPosition が TRUE ならば uItem はインデックスだと判断し
FALSE ならば、uItem がメニューアイテムの ID であると判断されます
lpmii は、メニューの状態を表現している MENUITEMINFO 構造体のポインタです
この構造体の状態に、メニューがセットされます

関数が成功すると 0 以外、失敗すると 0 が返ります
/*resorce.h*/
#define IDM_RENA 40001
/*リソーススクリプト*/
#include "resource.h"

KITTY MENU {
        POPUP "Kitty on your lap" {
                MENUITEM "RENA" , IDM_RENA
        }
}
#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HMENU hmenu;
	static MENUITEMINFO menuInfo;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hmenu = GetMenu(hwnd);
		menuInfo.cbSize = sizeof (MENUITEMINFO);
		menuInfo.fState = MFS_UNCHECKED;
		return 0;
	case WM_COMMAND:
		menuInfo.fMask = MIIM_STATE;
		if (menuInfo.fState == MFS_CHECKED)
			menuInfo.fState = MFS_UNCHECKED;
		else menuInfo.fState = MFS_CHECKED;
		SetMenuItemInfo(hmenu , IDM_RENA , FALSE , &menuInfo);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


非チェック状態で選択するとチェックされ
チェック状態で選択されるとチェックが解除されるメニュー項目です

こうすることで、メニューのチェック状態や無効、有効状態を操作できます
しかし、実はこの方法は 非常に面倒な 方法とも言えます
チェック状態などは、とくに TRUE や FALSE で操作できると便利だと考えられます

MENUITEMINFO 構造体を使った方法は、新しい方法で
現在でも多くのプログラムで CheckMenuItem() 関数などが見られます
これは、古い関数なので筆者は推奨しませんがこの関数の方が単純に処理できます
//この関数は紹介しません。気になるようであれば、ご自分で調べてください


メニューの状態を取得

メニューの状態をを MENUITEMINFO 構造体で表現し
これを用いてメニューの状態を様々に変化させることができました

逆に、MENUITEMINFO 構造体を使ってメニューの状態を知ることもできます
これには GetMenuItemInfo() 関数を使います
BOOL GetMenuItemInfo(
	HMENU hMenu , UINT uItem ,
	BOOL fByPosition , LPMENUITEMINFO lpmii
);
hMenu にはメニューのハンドル
uItem は、メニュー項目の ID またはインデックスを指定し
fByPosition で uItem が ID かインデックスかを判断します
lpmii は、MENUITEMINFO 構造体へのポインタです。これに情報が格納されます
関数が成功すれば 0 以外、失敗すれば 0 が返ります

この関数は SetMenuItemInfo() とまったく引数が同じですね
この関数を使って MENUITEMINFO 構造体の内容をセットし調べれば
指定したメニュー項目の状態を知ることができます
/*resorce.h*/
#define IDM_RENA 40001
/*リソーススクリプト*/
#include "resource.h"

KITTY MENU {
        POPUP "Kitty on your lap" {
                MENUITEM "RENA" , IDM_RENA
        }
}
#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HMENU hmenu;
	MENUITEMINFO menuInfo;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hmenu = GetMenu(hwnd);
		return 0;
	case WM_INITMENUPOPUP:
		menuInfo.cbSize = sizeof (MENUITEMINFO);
		menuInfo.fMask = MIIM_STATE;
		GetMenuItemInfo(hmenu , IDM_RENA , FALSE , &menuInfo);
		if (menuInfo.fState & MFS_GRAYED) 
			menuInfo.fState = MFS_ENABLED;
		else menuInfo.fState = MFS_GRAYED;
		SetMenuItemInfo(hmenu , IDM_RENA , FALSE , &menuInfo);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


ドロップダウンを表示するたびに、グレー状態と有効状態を変更するプログラムです
ちゃんと、メニューの状態が取得できていることが確認できますね
GetMenuItemInfo の場合でも cbSize と fMask メンバを設定することを忘れないで下さい


メニューの情報

プログラムが常にメニューの状態を把握している場合は必要ありませんが
動的に表現されたメニューであれば、当然原子プログラムで把握できる量は限りがあります

動的なメニューの実装方法は次章で説明することにしましょう
ここでは、メニューIDや項目の数を取得する方法を説明します
これで、プログラムが ID を把握で規定ない状態でも
メニュー項目の位置を指定することで、メニュー項目の ID を取得できます

メニュー項目の数を取得するには GetMenuItemCount() を使います
トップレベル、ポップアップメニューの項目数を取得できます

int GetMenuItemCount(HMENU hMenu);

hMenu にはメニューのハンドルを指定します
成功すればメニューの項目数が、失敗すれば -1 が返ります

メニュー項目の ID を取得したいなら GetMenuItemID() を使います

UINT GetMenuItemID(HMENU hMenu , int nPos);

hMenu は、メニューのハンドルを指定します
nPos には、メニュー項目の位置を最初のメニュー項目を0とするオフセットで指定します
成功すれば指定したメニュー項目の ID が返ります。メニュー項目のIDがNULLの場合や
指定した項目がサブメニューを開いている場合は 0xFFFFFFFF が返ります
/*リソーススクリプト*/
KITTY MENU {
	POPUP "Kitty on your lap" {
		MENUITEM "レナ" , 40001
		MENUITEM "ユキ" , 40002
		MENUITEM "ミミ" , 40003
	}
	POPUP "Magical nyan nyan TARUTO" {
		MENUITEM "たると" , 40004
		MENUITEM "シャルロッテ" , 40005
		MENUITEM "ちとせ" , 40006
		MENUITEM "柿ピー" , 40007
	}
}
#include <windows.h>

LPCTSTR strSub[] = {
	TEXT("Kitty on your lap") ,
	TEXT("Magical nyan nyan TARUTO")
};

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HMENU hmenu;
	static HWND list , edit , label;
	TCHAR strText[1024];
	int iID;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		list = CreateWindow(
			TEXT("LISTBOX") , NULL , 
			WS_CHILD | WS_VISIBLE | LBS_STANDARD , 
			0 , 0 , 200 , 50 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		SendMessage(list , LB_INSERTSTRING , 0 , (LPARAM)strSub[0]);
		SendMessage(list , LB_INSERTSTRING , 1 , (LPARAM)strSub[1]);

		edit = CreateWindow(
			TEXT("EDIT") , NULL , 
			WS_CHILD | WS_VISIBLE | WS_BORDER , 
			0 , 50 , 200 , 30 , hwnd , (HMENU)2 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);

		label = CreateWindow(
			TEXT("STATIC") , NULL , 
			WS_CHILD | WS_VISIBLE , 
			200 , 00 , 200 , 80 , hwnd , (HMENU)3 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		return 0;
	case WM_COMMAND:
		hmenu = 	GetSubMenu(GetMenu(hwnd) ,
			SendMessage(list , LB_GETCURSEL , 0 , 0)
		);

		GetWindowText(edit , strText , 2);
		iID = strText[0] & 0x0F;

		wsprintf(strText , TEXT("メニュー項目 = %d\nID = %d") ,
			GetMenuItemCount(hmenu) , GetMenuItemID(hmenu , iID)
		);
		SetWindowText(label , strText);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


リストボックスから対象となるポップアップメニューを選択し
下のエディットコントロールにインデックスを入力します
すると、右のスタティックコントロールに項目数と指定項目の ID が表示されます
上の図では、メニュー項目 「シャルロッテ」 の ID を表示しています

iID = strText[0] & 0x0F; では、ASCII 文字で表現されている数値をバイナリに変更しています
strText[0] に、ASCII コードの 0〜9 までが格納されていることを前提とします
このプログラムでは、入力ミスは考えないものとします


GetMenu()

HMENU GetMenu(HWND hWnd);

ウィンドウに割り当てられているメニューを取得します

hWnd - ウィンドウのハンドルを指定します

戻り値 - メニューハンドル。ウィンドウがメニューを持たないなら NULL

SetMenuItemInfo()

BOOL SetMenuItemInfo(
	HMENU hMenu , UINT uItem ,
	BOOL fByPosition , LPMENUITEMINFO lpmii 
);
メニューの状態を新しく設定します

hMenu - 対象のメニュー項目を持つメニューハンドルを指定します
uItem - 項目の ID またはインデックスを指定します
fByPosition - FALSE なら uItem は ID、それ以外ならインデックスであると判断します
lpmii - 変更情報を格納した MENUITEMINFO 構造体へのポインタを指定します

戻り値 - 成功すると 0 以外、失敗すると 0

GetMenuItemInfo()

BOOL GetMenuItemInfo(
	HMENU hMenu , UINT uItem ,
	BOOL fByPosition , LPMENUITEMINFO lpmii
);
メニューの状態を取得します

hMenu - 対象のメニュー項目を持つメニューハンドルを指定します
uItem - 項目の ID またはインデックスを指定します
fByPosition - FALSE なら uItem は ID、それ以外ならインデックスであると判断します
lpmii - 情報を格納する MENUITEMINFO 構造体へのポインタを指定します

戻り値 - 成功すると 0 以外、失敗すると 0

GetMenuItemCount()

int GetMenuItemCount(HMENU hMenu);

メニュー項目の数を取得します

hMenu - メニューのハンドルを指定します

戻り値 - メニューの項目数。失敗すれば -1

GetMenuItemID()

UINT GetMenuItemID(HMENU hMenu , int nPos);

メニュー項目の ID を取得します

hMenu - メニュー項目を持つメニューのハンドルを指定します
nPos - メニュー項目の位置を最初のメニュー項目を0とするオフセットで指定します

戻り値 - メニューの ID。NULL またはサブメニューを開いている場合は 0xFFFFFFFF



前のページへ戻る次のページへ