ポップアップ表示されるメニュー
マウスの右クリックで表示されるようなメニューの作り方

 

 この例のベースとしては、 スケルトンコード を使いました。

 

 ■ リソーススクリプト

 ・ ポップアップするメニューを作る場合は、POPUP "表示されない項目" BEGIN END で囲んで POPUP の項目にしなくてはなりません。

 ・ 面倒なのでヘッダファイルを使いません。両方のファイルに同じマクロ定義 #define をしています。

 


 
//
// resource-script "menu.rc"
// brc32 -iC:\borland\bcc55\include menu.rc menusample.exe 


#include <windows.h>

#define IDM_MENU1 101
#define IDM_MENU2 102
#define IDM_MENU3 103

MYMENU1 MENU DISCARDABLE 
BEGIN
    POPUP "表示されない項目"
    BEGIN
        MENUITEM "これを選択しても何もおこらないよ(&Dummy)"
        MENUITEM "メニュー1" , IDM_MENU1
        MENUITEM "これは選択できないよ" , IDM_MENU2 ,CHECKED,GRAYED
        MENUITEM SEPARATOR
        POPUP "新規作成(&New)"
        BEGIN
        MENUITEM "メニュー3" , IDM_MENU3
        END
    END
END


 

 ■ ソースコード

 ・ LoadMenu() によりまずメニューをロードして(ハンドルを得)、さらに GetSubMenu() でポップアップの項目 (これもハンドル) を取得しています。

 ・ WM_RBUTTONDOWN メッセージはマウスの右ボタン押下のイベントを教えてくれるメッセージです。同時に、マウスカーソルの位置の座標を持っているのでそれを利用します。この座標は「クライアント座標」 という親ウィンドウに対する相対座標です。

 ・ 一方、具体的にメニューを表示するアクションをおこす関数は TrackPopupMenu() 関数。この関数ではメニューの表示位置を指定してやります。指定は 「スクリーン座標」 といってディスプレイ上の座標でおこなえと指示されています。

 ・ だから、クライアント座標 → スクリーン座標 の変換を行う必要があります。ClientToScreen() 関数です。参考まで、逆の関数は ScreenToClient() 。MapWindowPoints() なども類似の関数です。

 ・ この例では WM_RBUTTONDOWN がマウスカーソルの座標を持っていることを巧く活用しています。でも、自分でこれではない方法でメニュー表示をやりたいときは必ずしもこのようにうまく座標が得られるとは限りません

 ・ その際は、GetCursorPos() 関数を使います。いつでもマウスカーソルの座標を得ることが出来ます。

 

 ※ 変数の宣言位置が処理部分と離れるとコードが理解しづらいものとなるので、処理部分は 一つのスコープ {...} として書き下しています。

 


// // program "menusample.cpp" // bcc32 -tW menusample.cpp #define _WIN32_WINNT 0x400 #define WINVER 0x400 #define _WIN32_IE 0x0501 //#define WIN32_LEAN_AND_MEAN #define STRICT #include <windows.h> #define IDM_MENU1 101 // 今回使う定数はこれだけ. #define IDM_MENU2 102 #define IDM_MENU3 103 BOOL InitApp( HINSTANCE ) ; BOOL InitWnd( HINSTANCE, int ) ; LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; HINSTANCE hAppInst ; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hInst_prev, LPSTR lpszCmdLine, int nCmdShow ) { BOOL ret ; MSG msg ; if ( ! hInst_prev ) { if ( ! InitApp( hInstance )) return FALSE ; } if ( ! InitWnd( hInstance, nCmdShow )) return FALSE ; while ( 0 != ( ret = GetMessage( &msg, NULL, 0, 0 ))) { if ( ret != -1 ) { TranslateMessage( &msg ) ; DispatchMessage( &msg ) ; } } return ( msg.wParam ) ; } BOOL InitApp( HINSTANCE hInstance ) { WNDCLASS wc ; wc.style = CS_HREDRAW | CS_VREDRAW ; wc.lpfnWndProc = WndProc ; wc.cbClsExtra = 0 ; wc.cbWndExtra = 0 ; wc.hInstance = hInstance ; wc.hIcon = ( HICON ) LoadIcon ( NULL, IDI_APPLICATION ) ; wc.hCursor = ( HCURSOR ) LoadCursor( NULL, IDC_ARROW ) ; wc.hbrBackground = ( HBRUSH ) GetStockObject( LTGRAY_BRUSH ) ; wc.lpszMenuName = NULL ; // 今回のメニューはメインのメニューとして使うものではないので、ここでは NULL (メニューなし) を指定しています. // でも、wc.lpszMenuName = "MYMENU1" ; とするのも可能です。どのようになるかは確かめてみてください. wc.lpszClassName = ( LPCTSTR ) "MyApp_Classification_1" ; hAppInst = hInstance ; return( RegisterClass( &wc )) ; } BOOL InitWnd( HINSTANCE hInstance, int nCmdShow ) { HWND hWnd ; hWnd = CreateWindow( (LPCTSTR) "MyApp_Classification_1", (LPCTSTR) "My Project", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT , CW_USEDEFAULT, CW_USEDEFAULT , NULL, NULL, hInstance, NULL ) ; if ( ! hWnd ) return FALSE ; ShowWindow( hWnd, nCmdShow ) ; UpdateWindow( hWnd ) ; SetFocus( hWnd ) ; return TRUE ; } LRESULT CALLBACK WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_CREATE : break ; case WM_RBUTTONDOWN : { POINT pt ; pt.x = LOWORD( lParam) , pt.y = HIWORD( lParam) ; // pt.x =... ; pt.y =... ; でも同じ. ClientToScreen( hWnd , &pt ) ; HMENU hMenu = LoadMenu( hAppInst ,"MYMENU1" ) ; if( ! hMenu ) MessageBox( hWnd , "most likely cause is mis-description in .rc file.\r\n especially the name of the\"menu\"" , "ERROR" , MB_ICONWARNING | MB_TASKMODAL ) ; HMENU hSubMenu = GetSubMenu( hMenu ,0 ) ; // この 0 はメニューのスクリプト中 "一番初め" POPUP 項目であることを示しています. TrackPopupMenu( hSubMenu ,TPM_LEFTALIGN ,pt.x, pt.y ,0 , hWnd, NULL) ; DestroyMenu( hMenu ) ; } break ; case WM_COMMAND : switch( LOWORD( wParam )) { case IDM_MENU1 : MessageBox( hWnd , "ありきたりなサンプルですが、すなわちメニューおされました。" , "Menu Item Selection is sensed." , MB_OK ) ; // ここ、case IDM_MENU1 break ; の間には、メニューが選択されたときにやりたい好きな処理を書く. break ; default : return( DefWindowProc( hWnd, msg, wParam, lParam )) ; } break ; case WM_CLOSE : DestroyWindow( hWnd ) ; break ; case WM_DESTROY : PostQuitMessage( 0 ) ; break ; default : return( DefWindowProc( hWnd, msg, wParam, lParam )) ; } return 0L ; }




 一箇所くらい間違えるかなとおもったのですが一発でコンパイルがとおったので、わたしは少し誇らしげです。

 bcc コンパイラでは、コンパイルは、
bcc32 -tW menusample.cpp のあとに続けて、
brc32 -iC:\borland\bcc55\include menu.rc menusample.exe です。

 

 これも「メモ帳」でちゃんとやってみました。こうなります。.

 

 混乱しないためにわたくしがどんな順序で書いたか説明すると、まず、赤い部分を一気に書いてしまいます。

 緑の部分はまったく書かなくても、単にメニューが選択されたときの動作がなにも起きないというだけなので問題ありません。

 赤い部分では締めくくりとして DestroyMenu() を忘れないようにします。

 TrackPopupMenu() は、メニューが選択される(押される)まで制御は返りません。

 メニューが選択、WM_COMMAND を親ウィンドウに "送る" とすぐ ( 多分 WM_COMMAND が "返る" までもなく)、TrackPopupMenu() の直後からプログラムの流れが再開されます。返ったらすぐ DestroyMenu() 。流れを追う場合はこのように見ることができます。

 

 TrackPopupMenu() の拡張版、TrackPopupMenuEx() 関数もあります。ツールバーの小さいドロップダウンのボタンを押したときのメニュー表示の際使用したことがあります。

 リソーススクリプトを用いなくても、ポップアップするメニューを作ってしまうことが比較的容易です。CreatePopupMenu() 関数。

 

 

[トップ]