第42章 メニューにBMP表示


まずは、今回作るプログラムを見てください。

これは、単にBMP画像を表示しているだけじゃないの?

いいえ、これはBMP一つ一つがメニューアイテムになっています。 試しに、最初の画像をクリックしてみましょう。



おおっ!第2階層のメニューが出てきたぞ!

これは、きっとメニューをオーナードローしたに違いない。

いいえ。第1階層目のメニューはオーナードローできません。(多分ね)

これは、CreateMenu関数で作ったメニューです。

では、CreateMenuでどのように作っていくのかを見てみましょう。

HMENU CreateMenu(VOID)


これで、第1階層(メニューバーに表示されるもの)のメニューが作れます。 戻り値がメニューハンドルとなります。 次に、第2階層以下の(ポップアップ)メニューは

HMENU CreatePopupMenu(VOID)

で作ります。 さて、ヘルプには次のようなことが書かれています。

「そのメニューがあるウィンドウに割り当てられていない場合には、 アプリケーションは終了する前にメニューに関連付けられている システム リソースを解放(DestroyMenu)しなければなりません。」

筆者は、この「ウィンドウに割り当てられていない」という意味が 今ひとつわからなかったのですが、今回の例ではメニューは親ウィンドウに 関連づけられています。従って解放しなくてもよいと思われます。 アプリケーション終了直前に解放しても問題はありません。 (DestroyMenu関数は成功する) ここで、この意味を今一度はっきり確かめる意味で、次のような プログラムを作ってみました。まず、オーバーラップ子供ウィンドウを 作成し、この子どもウィンドウに今回と同様の方法でメニューを持たせます。 次に、この子供ウィンドウを終了させた後、DestroyWindow関数を実行すると 失敗します。子供ウィンドウが終了するときに自動的に メニューの後始末をしてくれているようです。 子供ウィンドウが存在するときにDestroyWindowを実行すると この関数は成功します。しかし、メニューがおかしくなります。 (メニューバーの部分が他のウィンドウに隠されたときなど。 実際に実験してみればすぐにわかります。)

これに、AppendMenu関数でメニューアイテムを付け足していきます。 AppendMenu関数はすでに第12章で出てきました。 さて、メニューアイテムのうち ポップアップ項目にはID値はありませんでした。 このあたりの書き方を具体例で見てみましょう。

hMyMenu = CreateMenu(); hMyMenu2 = CreatePopupMenu(); AppendMenu(hMyMenu2, MF_ENABLED | MF_STRING ,IDM_CAT1, "名前");//(1) AppendMenu(hMyMenu2, MF_ENABLED | MF_STRING, IDM_CAT2, "年齢");//(2) AppendMenu(hMyMenu, MF_ENABLED | MF_BITMAP | MF_POPUP , (UINT)hMyMenu2, MAKEINTRESOURCE(hBMP1));//(3) AppendMenu(hMyMenu, MF_ENABLED | MF_BITMAP, IDM_CAT3, MAKEINTRESOURCE(hBMP2));//(4) AppendMenu(hMyMenu, MF_ENABLED | MF_BITMAP, IDM_CAT4, MAKEINTRESOURCE(hBMP3));//(5)

ポップアップメニューのメニューアイテム(1)(2)は普通に作ります。 次に、第1階層のメニュー項目のうち(3)はポップアップ項目です。従って IDはありません。3番目の引数はIDの代わりにその下に来るポップアップメニューの ハンドルを記述します。(4)(5)はそのしたにポップアップメニューがないので 普通通り?IDを記述します。

さて、メニュー項目を文字列ではなくBMPにするには、もうお気付きのこととおもいますが、 2番目の引数をMF_STRINGの代わりにMF_BITMAPにすればよいですね。

また、AppendMenuの最後の引数には、ビットマップハンドルを使いますが、 ただ単にビットマップハンドルを書いてもだめです。 ハンドルは単なる整数なので、これを変換してやる必要があります。 MAKEINTRESOURCEマクロを使います。32ビット版では、winuser.hのなかで

#define MAKEINTRESOURCE MAKEINTRESOURCEA

と定義されています。また、MAKEINTRESOURCEAは

#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))

という具合になっています。 これじゃよくわかりませんね。実は第39章 で「メニューIDはダブルクォーテーションで...」というように書いてありますが これをダブルクォーテーションで囲まないとメニューIDが単なる数値になってしまいます。 (resource.hの中で適当な数値にdefineされます) 囲まなかった場合は、wc.lpszMenuName = "MYMENU"などという使い方はできなくなります。 この場合は、wc.lpszMenuName = MAKEINTRESOURCE(MYMENU);とすれば解決します。

ポップアップ項目にするにはMF_POPUPを加えます。

さて、ビットマップ画像をAppendMenuで取り込むには、まず、ビットマップリソースを 作ります。具体的に例を示します。(VC++5.0の場合。他のバージョンでもほぼ同様)

0.あらかじめ100*100の大きさのビットマップ画像を用意しておき、クリップボード   にコピーしておきます。(正方形でなくてもよいが高さがまちまちだとかっこ悪い   メニューになってしまいます)   また、当然新規プロジェクトを作っておきます。 1.ツールバーの「ビットマップの新規作成」をクリックします。   (リソースのツールバーが出ていないときは「ツール」「カスタマイズ」でリソース    ツールバーを表示させます。) 2.余白をダブルクリックすると「ビットマッププロパティ」というダイアログボックスが   出てくるので、IDを"MYBMP1"などと入力します。また、色数を設定します。   ファイル名はIDをダブルクォーテーション付きで設定するとmybmp1.bmpなどと   いうように自動的につけてくれます。 3.「編集」「貼り付け」でリソースの出来上がりです。 4.「ファイル」「名前を付けて保存」で適当なファイル名(*.rc)をつけて保存します。 5.「プロジェクト」「プロジェクトに追加」で先ほどの*.rcを追加します。

さて、ビットマップリソースができあがったらビットマップハンドルを取得する必要があります。 第26章を参照してみてください。

hBMP1 = LoadBitmap(hInst, "MYBMP1");

というような感じですね。インスタンスハンドルの取得方法は 第19章を見てください。

さて、メニューの準備が整ったらSetMenu関数で親ウィンドウに セットします。

BOOL SetMenu( HWND hWnd, HMENU hMenu );

AppendMenuを使ったら必ずDrawMenuBar関数を呼びなさいと ヘルプに書かれています。 これで、メニューは表示されます。メニューが選択されてからの 処理は普通のメニューと同じです。 では、サンプルのプログラムを見てみましょう。

// owndr02.cpp #define STRICT #include <windows.h> //本当はヘッダーファイルを作ってdefineしてください #define IDM_CAT1 1000 #define IDM_CAT2 1010 #define IDM_CAT3 1020 #define IDM_CAT4 1030 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "bmpmenu"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!hPrevInst) { if (!InitApp(hCurInst)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

いつもと同じです。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメニュー", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

ここも、ほとんど同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HMENU hMyMenu, hMyMenu2; static HBITMAP hBMP1, hBMP2, hBMP3; HINSTANCE hInst; switch (msg) { case WM_CREATE: //親ウィンドウが作られたらすぐにメニューを作る hInst = ((LPCREATESTRUCT)lp)->hInstance; hBMP1 = LoadBitmap(hInst, "MYBMP1"); hBMP2 = LoadBitmap(hInst, "MYBMP2"); hBMP3 = LoadBitmap(hInst, "MYBMP3"); hMyMenu = CreateMenu(); hMyMenu2 = CreatePopupMenu(); AppendMenu(hMyMenu2, MF_ENABLED | MF_STRING ,IDM_CAT1, "名前"); AppendMenu(hMyMenu2, MF_ENABLED | MF_STRING, IDM_CAT2, "年齢"); AppendMenu(hMyMenu, MF_ENABLED | MF_BITMAP | MF_POPUP , (UINT)hMyMenu2, MAKEINTRESOURCE(hBMP1)); AppendMenu(hMyMenu, MF_ENABLED | MF_BITMAP, IDM_CAT3, MAKEINTRESOURCE(hBMP2)); AppendMenu(hMyMenu, MF_ENABLED | MF_BITMAP, IDM_CAT4, MAKEINTRESOURCE(hBMP3)); SetMenu(hWnd, hMyMenu); DrawMenuBar(hWnd); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_CAT1: MessageBox(hWnd, "猫の名前はマイケルです", "名前", MB_OK); break; case IDM_CAT2: MessageBox(hWnd, "猫の年齢は2歳です", "年齢", MB_OK); break; case IDM_CAT3: MessageBox(hWnd, "この猫はマイケルです", "マイケル", MB_OK); break; case IDM_CAT4: MessageBox(hWnd, "この猫もやっぱりマイケルです", "マイケル", MB_OK); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: DeleteObject(hBMP1); DeleteObject(hBMP2); DeleteObject(hBMP3); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

メニューが選択されたときの処理は、全部メッセージボックス を表示しているだけです。もう少しいろいろ変えてみてください。

[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Jun/07/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。