オーナードロー(オーナー描写)を使って、右クリック時のポップアップメニューを作成します。
オーナードローは、アイテムの描画処理をプログラム側(オーナー)で行います。
今回作成するアプリケーションは、メニューの表示はできますが、メニューの選択はできません。メニューの上にマウスをのせても、
色が反転しません。
メニュー - インコのWindowsSDK で作成したソースを修正します。
流れ
WM_CREATE
1.CreatePopupMenu()でドロップダウンメニュー、サブメニュー、ショートカットメニューのいずれかを作成。メニューは初期状態では空です。
2.AppendMenu()でメニューの末尾に新しいメニュー項目を追加。 InsertMenuItem()を使うことが推奨されていますが、ここでは、簡単に使える、AppendMenu()を使用します。
BOOL AppendMenu( HMENU hMenu, // メニューハンドル UINT uFlags, // オプションフラグ UINT uIDItem, // アイテムIDまたはサブメニューハンドル PCTSTR pItem // 表示する文字列 );
hMenu | メニューのハンドル | |
uFlags | メニューアイテムのオプション | |
MF_STRING MF_ENABLED MF_UNCHECKED |
デフォルト表示 | |
MFS_GLAYED MFS_DISABLED |
アイテムを灰色で表示し、選択不可に | |
MF_BITMAP | ビットマップを使用 | |
MFS_CHECKED | チェックマークつき | |
MF_POPUP | ドロップダウンメニューまたはサブメニューを開くアイテムを追加 | |
MF_MENUBARBREAK |
メニューアイテムを新しい行に追加 新しい列との間に線が引かれます。(メニューバーを除く) |
|
MF_MENUBREAK |
メニューアイテムを新しい行に追加 新しい列との間に線が引かれません。 |
|
MF_OWNERDRAW | オーナードロー | |
MF_SEPARATOR | セパレーター(区切り線)を表示 | |
uIDItem | アイテムのIDを指定 ・uFlagsメンバでMF_POPUPが指定されているとき・・・メニューのハンドルを指定 |
|
pItem | メニューアイテムに表示する文字列のアドレス ・uFlagsメンバでMF_BITMAPが指定されているとき・・・ビットマップのハンドル ・uFlagsメンバでMF_OWNERDRAWが指定されているとき・・・メニューアイテムに関する追加データを取得するために使用されるアプリケーション定義の値を指定します。 (メニューが作成・表示の更新の際に送られるWM_MEASUREITEMおよびWM_DRAWITEMメッセージのlParamパラメータが指す構造体のitemDataメンバの値) |
WM_MEASUREITEM
WM_MEASUREITEMメッセージは、コントロール(ここではメニュー)が作成されたときに、
コントロール(ここではメニュー)の数だけ実行されます。
1.WM_MEASUREITEMメッセージのlParamは、MEASUREITEMSTRUCT構造体へのポインタなので、これを用意した変数にセット。
typedef struct tagMEASUREITEMSTRUCT { UINT CtlType; // コントロールの種類 UINT CtlID; // コンボボックス・リストボックス・ボタンのコントロールID UINT itemID; // メニューアイテム・可変高さのリストボックス・コンボボックスの項目ID UINT itemWidth; // メニュー項目の幅 UINT itemHeight; // リストボックス、コンボボックス、メニューの各項目の高さ DWORD itemData // アイテムデータ } MEASUREITEMSTRUCT;
2.GetDC()で、スクリーン全体のデバイスコンテキストのハンドルを取得
HDC GetDC( HWND hWnd // ウィンドウハンドル );
3.GetTextExtentPoint32()で、文字列の幅と高さを計算
BOOL GetTextExtentPoint32( HDC hdc, // デバイスコンテキストのハンドル LPCTSTR lpString, // 文字列 int cbString, // 文字列内の文字数 LPSIZE lpSize // 文字列のサイズ(幅と高さ)を受け取るSIZE構造体へのポインタ );
この関数を実行すると、lpSizeメンバに指定したSIZE構造体に文字列の幅と高さがセットされます。
typedef struct tagSIZE { LONG cx; LONG cy; } SIZE;
4.GetTextExtentPoint32()で、セットした構造体から、文字列の幅と高さを LPMEASUREITEMSTRUCT構造体の、itemWidthメンバと、itemHeightメンバにセット
BOOL GetTextExtentPoint32( HDC hdc, // デバイスコンテキストのハンドル LPCTSTR lpString, // 文字列 int cbString, // 文字列内の文字数 LPSIZE lpSize // 文字列のサイズ(幅と高さ)を受け取る構造体へのポインタ );
5.ReleaseDC()で、デバイスコンテキストを解放。
int ReleaseDC( HWND hWnd, // ウィンドウハンドル HDC hDC // デバイスコンテキストのハンドル );
WM_DRAWITEM
WM_DRAWITEMメッセージは、オーナードローをしたアイテムが、描画・再描画されるときに実行されます。
今回の場合は、メニューの1つの項目を描画・再描画されるときに実行されます。
1.WM_DRAWITEMメッセージのlParamは、LPDRAWITEMSTRUCT構造体へのポインタなので、これを用意した変数にセット。
typedef struct tagDRAWITEMSTRUCT { UINT CtlType; // コントロールの種類 UINT CtlID; // コンボボックス、リストボックス、ボタンのコントロールID。 // メニューでは、CtlIDメンバは使用されません。 UINT itemID; // メニューのメニュー項目ID // またはリストボックスやコンボボックスの項目のインデックス UINT itemAction; // 要求される描画動作を定義 UINT itemState; // 現在の描画動作が行われた後の項目の表示状態を指定 HWND hwndItem; // コンボボックス、リストボックス、ボタンのコントロールのウィンドウハンドル HDC hDC; // デバイスコンテキスト RECT rcItem; // 描画されるコントロールの境界を定義する四角形 ULONG_PTR itemData; // アイテムデータ } DRAWITEMSTRUCT, NEAR *PDRAWITEMSTRUCT, FAR *LPDRAWITEMSTRUCT;
2.LPDRAWITEMSTRUCT構造体の、rcItemメンバ(RECT構造体)と、hDCメンバを別に用意した変数にセット。
3.LPDRAWITEMSTRUCT構造体の、itemDataメンバが指定するコントロールの項目 (今回の場合は、メニュー項目)ごとに、TextOut()で、文字列を表示します。
今回の場合、TextOut()の第2引数nXStart、第3引数nYStartには、LPDRAWITEMSTRUCT構造体のrcItemメンバを使用します。
WM_RBUTTONDOWN
1.ClientToScreen()で、 指定されたウィンドウ上の点の座標を、クライアント領域の座標からスクリーン座標に変換
2.TrackPopupMenu()でメニュー表示
ソースコードの入力
ソースコードは下記のように入れてください。
#include <windows.h> #include "resource.h" // グローバル変数: HMENU hMenu; // メニューのハンドル LPMEASUREITEMSTRUCT lpMI; // LPMEASUREITEMSTRUCT構造体 LPDRAWITEMSTRUCT lpDI; // DRAWITEMSTRUCT構造体 // このコード モジュールに含まれる関数の宣言を転送します: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; MyRegisterClass(hInstance); // アプリケーションの初期化を実行します: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } // メイン メッセージ ループ: while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; } // // 関数: MyRegisterClass() // // 目的: ウィンドウ クラスを登録します。 // ATOM MyRegisterClass(HINSTANCE hInstance) { 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(NULL , IDI_APPLICATION); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_HP); wcex.lpszClassName = TEXT("HP"); wcex.hIconSm = LoadIcon(NULL , IDI_APPLICATION); return RegisterClassEx(&wcex); } // // 関数: InitInstance(HINSTANCE, int) // // 目的: メイン ウィンドウを作成します。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(TEXT("HP"), TEXT("HP"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // 関数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: メイン ウィンドウのメッセージを処理します。 // // WM_COMMAND - アプリケーション メニューの処理 // WM_DESTROY - 中止メッセージを表示して戻る // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; HDC hdc; RECT rc; POINT pt; SIZE sz; LPCTSTR str[] = {TEXT("メニュー1"), TEXT("メニュー2"), TEXT("メニュー3")}; switch (message) { case WM_CREATE: hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_OWNERDRAW, IDM_MENU1, (LPCTSTR)0); AppendMenu(hMenu, MF_OWNERDRAW, IDM_MENU2, (LPCTSTR)1); AppendMenu(hMenu, MF_OWNERDRAW, IDM_MENU3, (LPCTSTR)2); break; case WM_MEASUREITEM: lpMI = (LPMEASUREITEMSTRUCT)lParam; hdc = GetDC(hWnd); GetTextExtentPoint32(hdc, str[lpMI->itemData], lstrlen(str[lpMI->itemData]) - 1, &sz); lpMI->itemWidth = sz.cx; lpMI->itemHeight = sz.cy; ReleaseDC(hWnd, hdc); break; case WM_DRAWITEM: lpDI = (LPDRAWITEMSTRUCT)lParam; rc = lpDI->rcItem; hdc = lpDI->hDC; switch (lpDI->itemID) { case IDM_MENU1: TextOut(hdc, rc.left, rc.top, str[0], lstrlen(str[0])); break; case IDM_MENU2: TextOut(hdc, rc.left, rc.top, str[1], lstrlen(str[1])); break; case IDM_MENU3: TextOut(hdc, rc.left, rc.top, str[2], lstrlen(str[2])); break; } break; case WM_RBUTTONDOWN: pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); ClientToScreen(hWnd, &pt); TrackPopupMenu(hMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 選択されたメニューの解析: switch (wmId) { case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
上記の太線で示している箇所のみ追加です。
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDM_MENU1 111
#define IDM_MENU2 112
#define IDM_MENU3 113
#define IDC_HP 109
上記の太線で示している箇所のみ追加です。
#include "resource.h" ///////////////////////////////////////////////////////////////////////////// // // メニュー // IDC_HP MENU BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "アプリケーションの終了(&X)", IDM_EXIT END POPUP "ヘルプ(&H)" BEGIN MENUITEM "バージョン情報(&A)...", IDM_ABOUT END END