メニューとポップアップメニュー

メニューとポップアップメニュー

続いて、代表的なリソースとして、メニューと、ポップアップメニューについて説明します。メニューとは、ウィンドウの上側についているメニューのことで、ほとんどのウィンドウズアプリケーションには、メニューがついています。そして、ポップアップメニューとは、アプリケーションのウィンドウの上でマウスを右クリックするとあらわれるメニューです。

サンプルプログラム

次のプログラムは、メニューとポップアップメニューを使用するサンプルです。まずは、以下のプログラムを入力してください。ただし、前回同様、プログラムの入力が完了したら、実行する前に対応するリソースを作る必要があります。

win32exproj2-1:WinMain.cpp
// ************************************
// Ex メニュー・ポップアップメニュー
// ************************************
//必要なヘッダーファイルのインクルード
#define STRICT

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>

//	リソースの読み込み
#include "resource.h"

// シンボル定義及びマクロ
#define WINDOW_WIDTH    800
#define WINDOW_HEIGHT   600

//	インスタンス(グローバル変数)
HINSTANCE hInst;

//	ウィンドウプロシージャのコールバック関数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
	static TCHAR szWindowClass[] = _T("Sample02");
	static TCHAR szTitle[] = _T("メニュー・ポップアップメニューのサンプル");

	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

	if (!RegisterClassEx(&wcex))
	{
		MessageBox(NULL,
			_T("RegisterClassExの処理に失敗しました"),
			_T("Sample02"),
			NULL);

		return 1;
	}

	hInst = hInstance; // グローバル変数に値を入れる

	// The parameters to CreateWindow explained:
	// szWindowClass				: アプリケーションの名前
	// szTitle						: タイトルバーに現れる文字列
	// WS_OVERLAPPEDWINDOW			: 生成するウィンドウのタイプ
	// CW_USEDEFAULT, CW_USEDEFAULT	: 最初に置くポジション (x, y)
	// WINDOW_WIDTH, WINDOW_HEIGHT	: 最初のサイズ (幅, 高さ)
	// NULL							: このウィンドウの親ウィンドウのハンドル
	// NULL							: メニューバー(このサンプルでは使用せず)
	// hInstance					: WinMain関数の最初のパラメータ
	// NULL							: WM_CREATE情報(このアプリケーションでは使用せず)
	HWND hWnd = CreateWindow(
		szWindowClass,
		szTitle,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		WINDOW_WIDTH, WINDOW_HEIGHT,
		NULL,
		NULL,
		hInstance,
		NULL
		);
	//	ウィンドウが生成できなかった場合
	if (!hWnd)
	{
		MessageBox(NULL,
			_T("ウィンドウ生成に失敗しました!"),
			_T("Sample02"),
			NULL);
		return 1;
	}

	// ウィンドウの表示に必要なパラメータ:
	// hWnd		: CreateWindowの戻り値
	// nCmdShow	: WinMainの引数の4番目
	ShowWindow(hWnd,
		nCmdShow);
	UpdateWindow(hWnd);

	// メインのメッセージループ:
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return (int)msg.wParam;
}

//	ウィンドウプロシージャ(メッセージに対するコールバック関数)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //ポイント構造体
    POINT    pt;
	switch (message)
	{
    //キーを押した
    case WM_KEYDOWN:
        switch(wParam)
        {
        case VK_ESCAPE:
            //終了メッセージを発生させる
            PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
        }
        break;
    //ウインドウが生成されたときに1度だけ通過
    case WM_CREATE:
        break;
    //マウス右クリック
    case WM_RBUTTONDOWN:
        pt.x = LOWORD(lParam);
        pt.y = HIWORD(lParam);
        //クライアント座標をスクリーン座標へ変換
        ClientToScreen( hWnd, &pt);
        //ポップアップメニューを表示
        TrackPopupMenu( GetSubMenu( GetMenu( hWnd ), 0), TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);
        break;
    case WM_COMMAND:
        switch( LOWORD(wParam) )
        {
        case ID_40001:
            //バージョン(A)
            MessageBox(hWnd, _T("メニューの実装Ver1.0"), _T("バージョン情報"), MB_OK);
            break;
        case ID_40002:
            //終了(X)
            PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}

	return 0;
}

リソースの作成

アイコンやカーソルの場合と同様、メニューもまた、リソースとして作成します。以下の手順良ように行ってください。

(1) リソースの作成

ソリューションエクスプローラで「リソースファイル」フォルダを右クリックし、出てきたポップアップメニューから「追加」→「リソースの追加」を選択します。すると、以下のようなダイアログが出るので、Menuを選択し、「新規作成」ボタンを押しましょう。(図2-1.)

図2-1.ソリューションエクスプローラ

ソリューションエクスプローラ

すると、リソースに、メニューリソースが追加され、メニューの編集画面が出現します。メニューも、リソースの一つなので、リソースウィンドウを見ると、IDR_MENU1という識別子がついているのがわかります。(図2-2.)

図2-2.メニューの編集画面

メニューの編集画面

このプログラムを実行すると、「OK」ボタンのみが表示された、非常に簡素なメッセージボックスが出現します。OKボタンを押すと、メッセージボックスは消えて、プログラムは終了します。

(2) メニューの追加

出現した、何も記入されていないメニュー(灰色の部分)をクリックすると、文字が入力できるようになります。ここに、「ファイル(&F)」と入力します。これで、ファイルと言うメニューが追加されました。(図2-3.)

図2-3.ファイルメニューの追加

ファイルメニューの追加

すると、このメニューの下に、更に項目が入力できるようになります。ここに、「ダイアログ(&A)」、「終了(&D)」と入力しましょう。(図2-4.)

図1-4.ファイルメニューの中身を入力

ファイルメニューの中身を入力
(3) 識別子の確認

(2)で追加したメニューの項目には、それぞれ識別子(ID)がついているので、確認します。まずは、「ダイアログ(&A)」のものから確認しましょう。「ダイアログ(&A)」を右クリックすると、ポップアップメニューが現れるので、その中から「プロパティー」を選択します。(図2-5.)

図1-5.識別子の確認

識別子の確認

すると、プロパティが表示され、この項目の識別子が、ID_40001であることがわかります。同様に、「終了(&D)」に関しても、同じことをすると、識別子が、ID_40002であることがわかります。(図2-6.)

図2-6.メニュー項目の識別子① 図2-7.メニュー項目の識別子②
メニュー項目の識別子① メニュー項目の識別子②

これらの識別子は、カーソルやアイコンの場合と同様、ユーザーが任意に書き換えることができます。ですが、このサンプルではそのまま用いることにします。

プログラムの実行

以上で、準備は完了です。プログラムを実行すると、メニューに「ファイル(F)」とだけ表示されたウィンドウが表示されます。(図2-8.)

図2-8.プログラム実行結果

プログラム実行結果

ここで、この「ファイル」メニューをクリックすると、「ダイアログ(A)」と「終了(D)」という項目が出現します。(図2-9.)

図2-9.メニューの出現

メニューの出現

ここで、「ダイアログ」メニューを選択すると、以下のようなダイアログが出現します。(図2-10.)

図2-10.メニューから「ダイアログ」を選択した場合の実行結果

メニューから「ダイアログ」を選択した場合の実行結果

ここで、「終了」を選ぶと、プログラムが終了します。

また、実行中に、ウィンドウ上でマウスを右クリックすると、以下のようなポップアップメニューが出現します。(図2-11.)ここで出てくる項目を選んだ結果は、普通のメニューの場合と一緒です。

図2-11.ポップアップメニュー

ポップアップメニュー

プログラムの仕組み

リソースのインクルード

では、プログラムの流れを見てみましょう。従来の通り、まずはリソースの読み込みから開始されますresource.hを読み込むことによって利用可能になります。(13行目)

リソースのインクルード
#include "resource.h"

メニューリソースの読み込み

続いて、メニューの読み込みの方法について説明します。カーソルの場合と同様、起動前にメニューを読み出す必要があります。メニューの読み込みには、WNDCLASSEX構造体のlpszMenuNameに定義します。(44行目)

LoadCursor関数
wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);

メニューのイベント処理

あとは、メニューのイベント処理で、メニューのどの項目が選ばれたかによってイベント処理を行えば、メニューの処理は完了します。メニュー項目が選択されると、WM_COMMANDイベントが発生します。(137行目)

カーソルの読み出し
wcex.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));

その時選択されたメニューの識別子は、ウィンドウプロシージャのwParamの下位ビットに格納されています。(138行目)ここでは、ID_40001と、ID_40002が設定されているので、それぞれのケースについて処理を行います。これで、メニューに関する処理は終了です。

メニューによるイベント処理の分岐
switch( LOWORD(wParam) )  ← wParamの下位ビットから、メニューの識別子を取得
{
case ID_40001:  ← 識別子が、「バージョン」の場合の処理
    //バージョン(A)
    MessageBox(hWnd, _T("メニューの実装Ver1.0"), _T("バージョン情報"), MB_OK);
    break;
case ID_40002:  ← 識別子が、「終了」の場合の処理
    //終了(X)
    PostMessage(hWnd, WM_CLOSE, 0, 0);
    break;
}

ポップアップメニューの表示

続いて、ポップアップメニューの処理を説明します。ポップアップメニューでは、まずはウィンドウ上での右クリックのイベント(WM_RBUTTONDOWN)を感知する必要があります。(129行目)

次に、このマウスの座標を取得します。すでに説明したとおり、マウス座標は、ウィンドウプロシージャlParamに格納されています。X座標、Y座標は、それぞれ下位ビット、上位ビットに格納されているので、それを座標として取得します。(130行目~131号目)

通常アイコンの読み出し
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);

しかし、この座標はあくまでもクラインと座標系の座標です。ポップアップメニューは、画面上の座標、つまりスクリーン座標として取得する必要がるので、クライアント座標をスクリーン座標に変換します。(133行目)

あとは、この位置にポップアップメニューを出現させます。ポップアップメニューは、TrackPopupMenu関数を利用して出現させます。

TrackPopupMenu関数
BOOL TrackPopupMenu(
  HMENU hMenu,               // ショートカットメニューのハンドル
  UINT uFlags,                  // オプション
  int x,                            // 水平位置
  int y,                            // 垂直位置
  int nReserved,               // 予約済み、0 を指定する
  HWND hWnd,                // 所有側ウィンドウのハンドル
  CONST RECT *prcRect    // 無視される
);

以上により、カーソルおよびアイコンが設定できます。このサンプルでは、138行目でこの処理を行っています。

TrackPopupMenu関数の利用
TrackPopupMenu( GetSubMenu( GetMenu( hWnd ), 0), TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);

GetSubMenu関数、および、GetMenuで、このアプリで定義されたメニューを取得しています。そのため、メニューと同じ項目を持ったポップアップメニューが出力されます。