メニューバーを作る


トップレベルメニュー

Windows アプリケーションには、メニューという強力な機能がある
メニューは、お店のそれと同様にソフトがもつ機能をユーザーに知らせることができる

一般にメニューというと、タイトルバーの下にあるメニューバーを言います
メニューを作成するには、やはりリソーススクリプトを用いる方法が一般的です
メニューの生成には MENU 文を用います

menuID MENU [[optional-statements]] { item-definitions . . . }

menuID には、このメニューの識別子を指定します。文字列でも整数でもかまいません
optional-statemnents は、STRINGTABLE のものと同じです
item-definitions には、そのメニューが持つアイテムの定義です

MENU の傘下には POPUP が置かれます
これを定義することで、メニューバーに項目が表示されます

POPUP text, [[optionlist]] { item-definitions . . . }

text には、表示するポップアップの文字列を指定します
この文字列はダブルクォーテーション " で囲まれている必要があります

optionlist には、メニューアイテムの外見を指定するパラメータです
このオプションを指定すると、非常にバラエティにあふれるメニューを作れます

定数解説
CHECKED チェックを持つメニューアイテム
トップレベルのメニューには無効
GRAYED グレー表示で、入力を受けないメニュー
HELP ヘルプアイテム
INACTIVE 選択できないメニュー
MENUBARBREAK メニューバーのポップアップを次の行に配置します
MENUBREAK メニューアイテムを次の新しいラインに配置します

item-definitions には、このポップアップの傘下のメニューアイテムを定義します
ポップアップには、それぞれ選択項目であるメニューアイテムがあります
メニューアイテムは MENUITEM 文で定義します

MENUITEM text, result, [[optionlist]]
MENUITEM SEPARATOR

MENUITEM 文には、二つの形式があります
まず、上の形式から説明しましょう

text には、項目に表示するメニューアイテムの文字列です
result は、ユーザーがメニューアイテムを選択した時にウィンドウに通知するIDです
//これは必須ですが、Borland C++ Compiler 5.5 の BRCC32.EXE では省略可能
メニューアイテムは、選択されると WM_COMMAND をウィンドウプロシージャに渡します
optionlist は、POPUP のそれと同じです

MENUITEM SEPARATOR は、セパレータを配置します
セパレータは、メニュー項目を区分けするラインです

MENU 文で作成したメニューバーは、WNDCLASS 構造体の
lpszMenuName に、識別子を指定することで表示されます
/*リソーススクリプト*/
KITTY MENU {
	POPUP "cat girls" {
		MENUITEM "Kitty on your lap" , 40001
		MENUITEM "Tokyo mew mew" , 40002

		MENUITEM SEPARATOR

		POPUP "Magical nyan nyan TARUTO" {
			MENUITEM "たると" , 40003
			MENUITEM "シャルロッテ" , 40004
			MENUITEM "ちとせ" , 40005
		}
	}
	POPUP "Card Captor Sakura" {
		MENUITEM "さくら" , 40006
		MENUITEM "知世" , 40007
	}
}
今回、メッセージは扱わないので メニューID は適当でかまいません
本来、メッセージ処理を行う場合は別のヘッダファイルに定数を定めるべきです

このメニューの識別子 KITTY をWNDCLASS 構造体に設定します
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		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;
}


上のプログラムとリソーススクリプトをコンパイルしてリンクすると
このようなプログラムになりました

「Tokyo mew mew」 と 「Magical nyan nyan TARUTO」アイテムの間にあるのがセパレータです
注目してほしいのが、POPUP の定義のなかに POPUP が定義されていることです
このように POPUP は数段階にネスティングすることが可能なのです

メニューアイテムやポップアップには、オプションを指定できます
これらを指定すれば、メニューの初期状態などを指定することができます
/*リソーススクリプト*/
KITTY MENU {
	POPUP "Kitty on your lap" {
		MENUITEM "レナ" , 40001
	}
	POPUP "Di Gi Charat" , MENUBARBREAK {
		MENUITEM "でじこ" , 40002 , CHECKED
		MENUITEM "ぷちこ" , 40003 , GRAYED
		MENUITEM "ラビアンローズ" , 40004 , MENUBREAK
		MENUITEM "ピョコラ・アナローグ三世" , 40005 , INACTIVE
	}
	POPUP "Card Captor Sakura" {
		MENUITEM "さくら" , 40006
	}
}


先ほどのプログラムに対して、メニューしか変更していないので
リソースコンパイルだけした結果です(プログラムのコンパイルの必要はない)

CHECKED を見てわかる様に、メニューアイテムはチェックすることができます
これを実装するかどうかはメニューの目的によります
GRAYED は、見てのとおり表示をグレーにして入力不可の状態にします
これらをプログラムで動的に変化させる方法は後ほど紹介します

MENUBARBREAK は、ポップアップを次の段に移行しています
MENUBREAK は、メニューアイテムを次のラインに移動していますね

INACTIVE は、表示されていますが選択することはできません

また、メニューは Alt キー + 文字キーで操作 することができます
これは、デフォルトで Alt キー + アイテムの先頭文字 でアイテムが選択されます
しかし、メニューアイテムの名前に & を指定 すると直後の文字に下線がつき
Alt キーとその直後の文字(下線付き文字) でアイテムを選択できるようになります
/*リソーススクリプト*/
KITTY MENU {
	POPUP "FILE(&F)" {
		MENUITEM "新規作成(&N)" , 40001
		MENUITEM "開く(&O)" , 40002
		MENUITEM "終了(&X)" , 40003
	}
	POPUP "EDIT(&E)" {
		MENUITEM "切り取り(&T)" , 40004
		MENUITEM "コピー(&C)" , 40005
		MENUITEM "貼り付け(&P)" , 40006
	}
}


多くのソフトウェアで見かける一般的なメニューアイテムです
下線付き文字に該当する文字を Alt キーと合わせて押すとアイテムが選択されます


メニューリソースを読みこむ

上記したウィンドウのメニューバーの選択方法は
WNDCLASS の定義時に指定するという静的な方法でした

これ以外に、CreateWindow() 関数にもメニューを指定する引数があります
ここでメニューのハンドルを指定することで、WNDCLASS 構造体で定義されている
メニューとは別のメニューをウィンドウに指定することもできます

ただし、この場合はリソースをメモリに読みこむ必要がある
メニューリソースをメモリに読みこむには LoadMenu() 関数を使う

HMENU LoadMenu(HINSTANCE hInstance , LPCTSTR lpMenuName);

hInstance には、リソースが入っているモジュールのインスタンスハンドルを
lpMenuName は、メニューリソースの識別子を指定します

成功すればメニューのハンドルが、失敗すれば NULL が返ります
/*リソーススクリプト*/
KITTY MENU {
	POPUP "Kitty" {
		MENUITEM "Kitty on your lap" , 40001
	}
}
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		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	= NULL;
	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 , LoadMenu(hInstance , TEXT("KITTY")) ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
LoadMenu() 関数を用いて CreateWindow() 関数にメニューのハンドルを渡しています
この方法でも、問題なくメニューが表示されます
WNDCLASS で指定されているデフォルトのメニュー以外のメニューを使いたい時などは
このような方法で、別のメニューをウィンドウに割り当てることができます

よりダイナミックにメニューをウィンドウに与えたい場合
WNDCLASS 構造体や CreateWindow() 関数のメニューの指定が NULL でも
SetMenu() ファンクションによってウィンドウに指定することができます

BOOL SetMenu(HWND hWnd , HMENU hMen);

hWnd には、メニューを変更するウィンドウのハンドルを
hMen には、メニューのハンドルを指定します
ここにNULL を指定するとメニューが解除されます

成功すると 0 以外、失敗すると 0 が返ります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		SetMenu(hwnd , 
			LoadMenu(
				((LPCREATESTRUCT)(lp))->hInstance ,
				TEXT("KITTY")
			)
		);
		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	= NULL;
	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;
}
この方法でも、ウィンドウにメニューが与えられています
このプログラムのメニューリソースは、上のものと同じものを使っているとします


LoadMenu()

HMENU LoadMenu(HINSTANCE hInstance , LPCTSTR lpMenuName);

メニューリソースをメモリにロードします

hInstance - メニューリソースが入っているモジュールのインスタンス
lpMenuName - メニューの識別子を指定します

戻り値 - メニューのハンドル。失敗すれば NULL

SetMenu()

BOOL SetMenu(HWND hWnd , HMENU hMen);

指定されたウィンドウにメニューを割り当てます

hWnd - ウィンドウのハンドルを指定します
hMen - メニューのハンドルを指定します。NULLを指定するとメニューを解除します

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



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