ショートカットメニュー
任意の位置にメニューを表示する
メニューはメニューバーとしてウィンドウの上部に配置するほか、任意の場所に表示することができます。
多くのアプリケーションではクライアント領域を右クリックしたときに、マウスカーソルの位置に何らかのメニューが表示される作りになっています。
そのため「右クリックメニュー」とも呼ばれますが、(Windowsでは)正式にはショートカットメニューと言います。
「ポップアップメニュー」や「コンテキストメニュー」とも言います。
サブメニューの取得
ショートカットメニューはメニューバーとは違い「バー」が無く、メニューバー項目のクリック時にドロップダウン表示されるサブメニューのみを表示することになります。
サブメニューを取得するにはGetSubMenu
関数を使用します。
- HMENU GetSubMenu(
HMENU hMenu,
int nPos
); - メニューハンドルhMenuからnPos番のサブメニューを取得する。
存在しない場合はNULLを返す。
hMenu
はLoadMenu関数で取得されるメニューハンドルを指定します。
nPos
はサブメニューを開く親となるメニューの項目の番号です。
(最初は0)
TrackPopupMenuEx関数
実際にショートカットメニューを開くにはTrackPopupMenuEx
関数を使用します。
- BOOL TrackPopupMenuEx(
HMENU hMenu,
UINT uFlags,
int x,
int y,
HWND hWnd,
LPTPMPARAMS lptpm
); - ショートカットメニューhMenuを開く。
成功した場合は0以外を、失敗した場合は0を返す。
フラグuFlagsにTPM_RETURNCMDフラグを指定した場合、戻り値は選択したメニュー項目の識別子。
何も選択せずにメニューが閉じられた場合は0を返す。
hMenu
はGetSubMenu関数で取得されるサブメニューのハンドルです。
uFlags
はメニューの動作に関するフラグの指定です。
以下の定数の組み合わせを指定します。
定数 | 説明 |
---|---|
表示位置の指定 | |
TPM_LEFTALIGN | メニューの左端位置を引数x の位置にする(既定) |
TPM_CENTERALIGN | メニューの水平方向の中央位置を引数x の位置にする |
TPM_RIGHTALIGN | メニューの右端位置を引数x の位置にする |
TPM_TOPALIGN | メニューの上端位置を引数y の位置にする(既定) |
TPM_VCENTERALIGN | メニューの垂直方向の中央位置を引数y の位置にする |
TPM_BOTTOMALIGN | メニューの下端位置を引数y の位置にする |
メッセージ関連 | |
TPM_NONOTIFY | 親ウィンドウに一部の通知メッセージを送信しない ( WM_COMMAND は送信される) |
TPM_RETURNCMD | 選択した項目の識別子をこの関数の戻り値として返す ( WM_COMMAND は送信されなくなる) |
マウスボタンの動作 | |
TPM_LEFTBUTTON | マウスボタンの左クリックでのみ項目を選択可能 (既定) |
TPM_RIGHTBUTTON | マウスボタンの左または右クリックで項目を選択可能 |
アニメーション | |
TPM_HORPOSANIMATION | メニューを左から右にアニメーションする |
TPM_HORNEGANIMATION | メニューを右から左にアニメーションする |
TPM_VERPOSANIMATION | メニューを上から下にアニメーションする |
TPM_VERNEGANIMATION | メニューを下から上にアニメーションする |
TPM_NOANIMATION | アニメーションしない |
その他 | |
TPM_HORIZONTAL | 表示しようとするメニューの領域が除外領域(lptpm )に重なるとき、水平方向にずらして表示する。 |
TPM_VERTICAL | 表示しようとするメニューの領域が除外領域(lptpm )に重なるとき、垂直方向にずらして表示する。 |
TPM_RECURSE | 別のメニューが表示されている状態でもメニューを開く |
TPM_LAYOUTRTL | テキストを右から左に表示 |
アニメーションの設定に関してはWindowsの設定でメニューのアニメーション表示が有効になっている必要があります。
x
、y
はメニューを表示する位置の基準点となるX座標とY座標です。
これはスクリーン座標を指定します。
hWnd
はショートカットメニューを所有するウィンドウのハンドルです。
このウィンドウにメッセージが送られてきます。
lptpm
はメニューの表示を禁止する領域の指定です。
(スクリーン座標)
メニューはこの領域内に重なって表示されることはありません。
必要がない場合はNULL
を指定します。
データ型はTPMPARAMS構造体(のポインタ)で、以下のようになっています。
- typedef struct tagTPMPARAMS {
UINT cbSize;
RECT rcExclude;
} TPMPARAMS; - ウィンドウの配置時に除外する矩形を格納する構造体。
cbSize
メンバはこの構造体のサイズです。
(sizeof(TPMPARAMS)
を指定)
rcExclude
メンバはRECT構造体です。
TrackPopupMenuEx関数とほぼ同じものにTrackPopupMenu関数があります。
こちらを使用することも可能ですが、これから新規に作るプログラムではTrackPopupMenuEx関数の使用が推奨されます。
WM_CONTEXTMENUメッセージ
ショートカットメニューはクライアント領域を右クリックすることで表示されますが、「Shift + F10」キーにもこの機能が割り当てられています。
その他、キーボードに専用のキーが搭載されている機種もあります。
これらの操作ではWM_CONTEXTMENU
メッセージが送信されます。
WPARAMは右クリックしたウィンドウのハンドルです。
このハンドルは右クリックした位置によってはウィンドウ上の子ウィンドウ(コントロール)のハンドルである可能性があります。
LPARAMの下位ワードは右クリック時のマウスカーソルのX座標です。
上位ワードはマウスのY座標です。
(スクリーン座標)
下位ワード/上位ワードはLOWROD
/HIWORD
マクロで取得できますが、このメッセージの処理ではGET_X_LPARAM
/GET_Y_LPARAM
マクロを使用する必要があります。
(→マウス座標)
右クリック以外でこのメッセージを発生させたとき(「Shift + F10」キーなど)、マウスの座標情報は(-1, -1)という値になります。
これをLOWROD/HIWORDマクロで取得すると正数(プラス値)になってしまい、おかしな位置にショートカットメニューが表示されてしまいます。
GET_X_LPARAM/GET_Y_LPARAマクロならば負数(マイナス値)で取得できるので、負数の場合は位置を調整する処理をはさむことができます。
サンプルコード
以下にショートカットメニューのサンプルを示します。
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU1 MENU
BEGIN
POPUP "右クリック1"
BEGIN
MENUITEM "アイテム1", ID_40001
MENUITEM "アイテム2", ID_40002
MENUITEM "ショートカットメニュー2に切り替え", ID_40003
END
POPUP "右クリック2"
BEGIN
MENUITEM "アイテム3", ID_40004
MENUITEM "アイテム4", ID_40005
MENUITEM "ショートカットメニュー1に切り替え", ID_40006
END
END
#include <windows.h>
#include "resource.h"
//ウィンドウの生成等は省略
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HMENU hMenuP, hMenu1, hMenu2, hMenu;
POINT point;
switch (message)
{
case WM_CREATE:
hMenuP = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
hMenu1 = GetSubMenu(hMenuP, 0);
hMenu2 = GetSubMenu(hMenuP, 1);
hMenu = hMenu1;
break;
case WM_CONTEXTMENU: //ショートカットメニュー表示
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
if (point.x < 0) {//右クリック以外
//クライアント領域の左上に表示する
point.x = point.y = 0;
ClientToScreen(hWnd, &point);
}
TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN,
point.x, point.y, hWnd, NULL);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case ID_40003:
hMenu = hMenu2;
MessageBox(hWnd, L"ショートカットメニューを「2」に切り替えました。", L"情報", MB_OK);
break;
case ID_40006:
hMenu = hMenu1;
MessageBox(hWnd, L"ショートカットメニューを「1」に切り替えました。", L"情報", MB_OK);
break;
}
break;
case WM_DESTROY: //ウィンドウの破棄
DestroyMenu(hMenuP);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
クライアント領域内を右クリックするとショートカットメニューが表示されます。
「ショートカットメニュー○に切り替え」の項目を選択するごとにメニューの内容が交互に切り替わります。
まずLoadMenu
関数で親メニューハンドルを取得します。
親メニューは「右クリック1」「右クリック2」の2つの項目があり、それぞれ3つずつの項目を持つサブメニューを持ちます。
この親メニューからGetSubMenu
関数でサブメニューハンドルを2つ取得します。
WM_CONTEXTMENU
メッセージでは、ショートカットメニューを実際に表示する処理を記述します。
LPARAMから位置を取得し、正数だった場合は右クリックでの呼び出しなので、そのままの位置(マウスカーソルの位置)に表示します。
負数だった場合は、今回はクライアント領域の左上に表示することにします。
(→スクリーン座標とクライアント座標の変換)
アプリケーションによっては現在選択中のコントロールの位置に表示するなどすれば使いやすくなるでしょう。
TrackPopupMenuEx
関数ではTPM_LEFTALIGN
フラグとTPM_TOPALIGN
フラグを指定し、表示位置から右下方向にメニューが表示されるようにしています。
(これらのフラグを指定しなくても既定で同じ動作になります)
トとビット演算 ≫