【VBA×WindowsAPI】UserFormを右クリックした時にコンテキストメニューを表示させる

VBAのUserForm上で右クリックした時にコンテキストメニューを出したいという場面に出くわすことがあります。WindowsAPIにはメニューバーのメニューをはじめとした、メニューを作成するための機能が存在します。このうちポップアップメニューを作成する機能を使うことで、UserFormに対してのオリジナルのコンテキストメニューを作成することができます。

右クリックの検知自体はUserFormイベントを使っているだけなので、各コントロール別にメニューの内容を切り替えたりその他のイベントを利用してコンテキストメニューを表示させることも可能です。

UserFormにコンテキストメニューを作成する

WindowsAPIを使うことで、上画像のようにUserForm上を右クリックした際に自作のコンテキストメニューを表示させることができます。各メニュー項目には任意の文字列はもちろん水平線やアイコン画像、メニュー項目自体に画像を設定することが可能です。各メニュー項目には任意のIDを付与することができ、このIDによってユーザーが選択した項目を判定することができます。

VBAでUserFormにコンテキストメニューを作成するには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。

icon-check-square FindWindow関数               :ウィンドウのハンドルを取得する
icon-check-square CreatePopupMenu関数      :ポップアップメニューを作成する
icon-check-square DestroyMenu関数            :メニューを削除する
icon-check-square AppendMenu関数              :メニュー項目を追加する
icon-check-square GetCursorPos関数         :カーソルの座標を取得する
icon-check-square TrackPopupMenu関数       :ポップアップメニューを表示させる
icon-check-square SetMenuItemBitmaps関数:メニューにアイコン画像を設定する
icon-check-square LoadImage関数               :画像をメモリ上に読み込ませる
icon-check-square DeleteObject関数           
 :メモリ上の画像を削除する

「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。

サンプルコード

UserFormを右クリックした時に自作のコンテキストメニューを表示させるためのサンプルコードは下記の通りです。画像パス部分は読み込ませたいビットマップ画像のフルパスを入力して下さい。未入力や存在しないパスを入力した場合は画像の読み込みに失敗し、表示されるコンテキストメニューの画像部分に何も表示されなくなります。 (画像が表示されないだけでその他への影響は特にありません)

コード解説

icon-edit UserFormのウィンドウハンドルを取得

ウィンドウにはそれぞれウィンドウハンドルと呼ばれる、ウィンドウを識別するためのID情報のようなものが付与されています。このウィンドウハンドルを取得することで、”どの”ウィンドウに対して処理を行うのかを簡単に指示することができます。(Windows APIでは頻出ワードです)

UserFormもウィンドウの1つであるため一意のウィンドウハンドルを持ちます。ウィンドウハンドルを取得するための関数はいくつも存在しますがここでは常套手段のFindWindow関数を使ってウィンドウハンドルを取得しています。FindWindow関数は下記のように記載することで指定のクラス名もしくはウィンドウ名から該当のウィンドウへのハンドルを取得することができます。

icon-code  FindWindow関数 

hWnd = FindWindow(クラス名”, “ウィンドウ名”)

UserFormはクラス名が”ThunderDFrame”で、ウィンドウ名はMe.Captionとなります。いずれか片方を入力してもう一方の引数をvbNullStringとしても取得可能ですが、サンプルコードではどちらの引数も入力して確実にUserFormが取得できるようにしています。
  

icon-edit 画像をメモリ上に読み込み

コンテキストメニューに画像を設定したい場合はLoadImage関数を使ってBMP画像をメモリ上に読み込ませておく必要があります。LoadImage関数は引数によって画像を読み込む手段を変更することができますが、サンプルコードではファイルパスから指定のBMP画像を読み込む方法を採用しています。

下記のコードのsPathImg部分を任意のBMP画像のフルパスにすることでメモリ上にその画像を読み込ませることが出来ます。このとき、読み込ませた画像のハンドルが戻り値として取得できます。

icon-code  LoadImage関数 

hBmp = LoadImage(0, sPathImg, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)

読み込んだハンドルはそのプロセスが終了するとメモリ上から自動で解放されますが、DeleteObject関数を使って明示的にメモリから解放することもできます。サンプルコードではUserFormのTerminateイベントで明示的に解放していますが、この処理はなくても自動で解放されるので問題はありません。
  

icon-edit ポップアップメニュー作成

コンテキストメニューは利用可能な操作メニューを一覧化したもののことをいい、メニューの種類としてはポップアップメニューに属するものが一般的です。ポップアップメニューはCreatePopupMenu関数を使うことで作成ができます。戻り値は作成したポップアップメニューのハンドルです。

icon-code  CreatePopupMenu関数 

hMenu = CreatePopupMenu()

ここで作成されるポップアップメニューはメモリ上に作成されるものであり画面上には表示されません。また、メニュー項目や画像などが設定されていない”空のメニュー”の状態となっています。そのため、戻り値として取得したポップアップメニューのハンドルを使って、任意のメニュー項目を追加していく必要があります。このときメニュー項目に対してアイコン画像を付与することも可能です。
 

icon-edit メニューの設定

作成したポップアップメニューに項目を追加するにはAppendMenu関数を使います。

icon-code  AppendMenu関数 

Call AppendMenu(hMenu, wFlags, wIDNewItem, lpNewItem)

第1引数のhMenuには作成したポップアップメニューのハンドル、第2引数のwFlagsにはメニュー項目の内容(外観や動作)を定義するためのフラグ、第3引数のwIDNewItemには項目の識別ID、第4引数のlpNewItemには項目の内容をそれぞれ入力します。

wFlagsの代表的なフラグは下表の通りです。サンプルコードではこれらすべてのフラグを使って具体的なメニュー項目の設定を行ってるので、各引数の入力方法はサンプルコードを参照下さい。

フラグ (定数) 説明
MF_STRING (&H0) メニュー項目に文字列を設定する
MF_DISABLED (&H2) メニュー項目を選択不可状態にする
MF_BITMAP (&H4) メニュー項目にBMP画像を設定する
MF_CHECKED (&H8) メニュー項目をチェック付与状態にする
MF_POPUP (&H10) メニュー項目にサブメニューを付与する
MF_SEPARATOR (&H800) メニュー項目に水平分割線を設定する

アイコンの設定にはSetMenuItemBitmaps関数が別途必要になります。

icon-code  SetMenuItemBitmaps関数 

Call SetMenuItemBitmaps(hMenu, nPosition, 0, hBmp, 0)

第1引数のhMenuにはメニューハンドル、第2引数のnPositionにはアイコンを設定する項目の識別ID(AppendMenu関数で設定)、第4引数のhBmpには設定するBMP画像のハンドルをそれぞれ設定します。第3, 5引数は今回のケースでは0としていますが、これらはサブメニューを持つメニュー項目へのアイコン付与や、チェック付与時のチェック部分のアイコンを指定したい場合に利用します。

サブメニューはメインのポップアップメニューとは別にポップアップメニューを作成して、親のポップアップメニューに対してAppendMenu関数のMF_POPUPフラグで埋め込むことで作成が可能です。サンプルコードではサブメニュー内の識別IDを11,12としていますが、これは1つ下の階層のサブメニューであるということを表現しているだけであり、特にルールなく好きな値を設定することが可能です。
 

icon-edit コンテキストメニューの表示

前項まででメモリ上ではメニューの作成が完了した状態となっているので、ユーザーが操作できるように画面上に表示させる必要があります。コンテキストメニューは多くの場合、右クリックで表示されるためUserFormのMouseUpイベントで右クリックを検知して表示させます。

ポップアップメニューを画面上に表示させるにはTrackPopupMenu関数を使います。

icon-code  TrackPopupMenu関数 

Call TrackPopupMenu(hMenu, wFlags , X, Y, 0, hWnd, 0)

第1引数のhMenuには表示させるポップアップメニューのハンドル、第2引数には関数オプションのフラグ、第3,4引数のX,Yにはメニュー表示するスクリーン座標、第6引数のhWndにはメニューを所持させるウィンドウ(UserForm)のハンドル、第5,7引数には定数値「0」をそれぞれ設定します。

第2引数の関数オプションは様々ありますがここではTPM_RETURNCMDを設定しています。このフラグにより、ユーザーが選択したメニュー項目の識別IDを戻り値として取得することができるようになり、選択さえれた項目ごとに条件分岐処理を行うことができるようになります。

メニューを表示するX,Y座標はスクリーン座標(スクリーンの左上を原点とした座標)での値が必要であるため、GetCursorPos関数を使って右クリックされた際のカーソルの座標値を取得しています。

関連情報

icon-share-square VBA×WindowsAPIまとめページ

その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。

icon-share-square 参考

Microsoft公式:メニューについて – Win32 apps

Excel, VBA, Windows API

Posted by Lic