第45章 メニュー・オーナードロー その1


今回は、メニューのオーナードローについてします。 どんなことができるかというと、オーナーウィンドウ側で 操作するのでたいていのことが(?)できるはずです。 フォントを変えたり、色を付けたり、フォントの大きさを変える、グラフィックスを 表示するなどです。 ボタンのオーナードローについては第41章ですでにやっています。 ボタンの時は、WM_DRAWITEMメッセージが描画のタイミングでした。 メニューでも同じです。それと、メニューをオーナードローするときは もう一つ大事なメッセージがあります。それは、WM_MEASUREITEMメッセージです。

WM_MEASUREITEM idCtl = (UINT) wParam; // コントロール識別子 lpmis = (LPMEASUREITEMSTRUCT) lParam; // アイテムの大きさ情報

これは、コントロール(ボタンとか、メニューとか・・)が作成されたときに オーナーに送られます。オーナーは、lParamで示された、 MEASUREITEMSTRUCT構造体に必要な情報を格納します。 メニューから送られてきたときはwParamは0です。 では、MEASUREITEMSTRUCT構造体とはどんなものでしょうか。

typedef struct tagMEASUREITEMSTRUCT { // mis UINT CtlType; // コントロールタイプ UINT CtlID; // コンボボックス・リストボックス・ボタン識別子 UINT itemID; // メニューアイテム・可変高さのリストボックス・ // コンボボックス識別子 UINT itemWidth; // メニューアイテムの幅 UINT itemHeight; // メニューアイテムの高さ DWORD itemData; // アイテムデータ } MEASUREITEMSTRUCT;

要するに、WM_MEASUTREITEMメッセージが送られてきたら、lParam を調べてMEASUREITEMSTRUCT構造体にデータを入れてやります。 このときオーナードローするものがいっぱいあるときは、 switch文でコントロールタイプ別に種分けします。 (ODT_MENU, ODT_LISTBOX, ODT_BUTTON, ODT_COMBOBOX) つぎに、メニューならメニューアイテムID別に種分けをして アイテムの高さ、幅を代入してやります。オーナードローするものが 1種類だけならこの種分けの作業は不要です。 また、メニューの場合メニューアイテム識別子ごとに幅、高さを 設定しても一番大きいものにあわせられます。 (筆者のやり方がまずい?たとえばメューアイテムごとにフォントの 大きさを変えても、表示される領域は一番大きいフォントの高さに合わせられてしまう。) 最後のアイテムデータとは何でしょうか。VC++5.0のヘルプには

Specifies the application-defined 32-bit value associated with the menu item

と書いてあります。 簡単に言ってしまうとAppendMenu関数の最後の引数で使ったものです。 ふつうは、最後の引数はメニューアイテムの名前ですが、オーナードローの場合 AppendMenu関数の段階ではまだメニューアイテムを表示しません。 それでは、ここまででどんな感じで使うのか例を示します。

enum {MENU_END, MENU_DOG, MENU_CAT, MENU_RAT}; LPCTSTR str[] = {"終 了", "犬  ", "猫  ", "ネズミ"}; 途中略 case WM_CREATE: hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_OWNERDRAW, IDM_END, (LPCTSTR)MENU_END); AppendMenu(hMenu, MF_OWNERDRAW, IDM_DOG, (LPCTSTR)MENU_DOG); AppendMenu(hMenu, MF_OWNERDRAW, IDM_CAT, (LPCTSTR)MENU_CAT); AppendMenu(hMenu, MF_OWNERDRAW, IDM_RAT, (LPCTSTR)MENU_RAT); break; case WM_MEASUREITEM: lpMI = (LPMEASUREITEMSTRUCT)lp; hdc = GetDC(hWnd); hFont = SetMyFont("MS 明朝", 30); hFontOld = (HFONT)SelectObject(hdc, hFont); GetTextExtentPoint32(hdc, str[lpMI->itemData], lstrlen(str[lpMI->itemData]) - 1, &sz); lpMI->itemWidth = sz.cx; lpMI->itemHeight = sz.cy; SelectObject(hdc, hFontOld); DeleteObject(hFont); ReleaseDC(hWnd, hdc); return TRUE;

親ウィンドウが作られたらすぐに、CreatePopupMenuを呼び出します。 (オーナードローでは第1階層のメニューはできない) そして、メニュー項目をAppendMenuで足していきます。 通常は、最後の引数は"終了"とか、"オプション"などといった 文字列が来ますね。ここでは、MENU_ENDとか、MENU_DOGなどという 列挙子を使っています。これがメニューデータになります。 次に、WM_MEASUREITEMメッセージのところでMEASUREITEMSTRUCT構造体に 幅と高さのデータを渡します。ここで使っているGetTextExtentPoint32関数は

BOOL GetTextExtentPoint32( HDC hdc, // デバイスコンテキストハンドル LPCTSTR lpString, // 文字列のアドレス int cbString, // 文字数 LPSIZE lpSize // SIZE構造体のアドレス );

となっています。これでSIZE構造体にこの文字列が要する 大きさが格納されます。なお16ビット版ではこの関数は 使えません。代わりにGetTextExtent関数というのがあります。

DWORD GetTextExtent(hdc, lpszString, cbString) HDC hdc; /* デバイス コンテキストのハンドル */ LPCSTR lpszString; /* 文字列のアドレス */ int cbString; /* 文字列内のバイト数 */

これは、使い方が32ビット版とは少し違います。 戻り値の下位ワード(LOWORD)が幅を、上位ワード(HIWORD)が高さを表します。

どちらの関数も場合によっては大きさに誤差の出ることがあります。

ところで、SIZE構造体についてはまだ解説をしていませんでした。

typedef struct tagSIZE { // siz LONG cx; LONG cy; } SIZE;

のように定義されています。 16ビット版では、

typedef struct tagSIZE { int cx; int cy; } SIZE;

のように定義されています。

なお、上のプログラム例でSetMyFontというのは、 自作関数でフォントを設定して、戻り値として 今までのフォントハンドルを返すものです。

さて、次にWM_DRAWITEMメッセージが来たときに、 メニューを書けばよいのですが具体例を見てみましょう。

RECT rc; 途中略 case WM_DRAWITEM: lpDI = (LPDRAWITEMSTRUCT)lp; rc = lpDI->rcItem; hdc = lpDI->hDC; hFont = SetMyFont("MS 明朝", 30); hFontOld = (HFONT)SelectObject(hdc, hFont); 以下略

古いCコンパイラではrc = lpDI->rcItem;がエラーになることがあります。 昔は(K&Rの時代)構造体を丸ごとコピーすることはできませんでした。 K&Rが「将来は構造体丸ごとの代入ができるようになる」と予言したそうです。

DRAWITEMSTRUCT構造体については、第41章で少しだけ解説しました。

itemIDメンバは、メニューアイテム識別子です。itemStateメンバは必要な描画動作を示しています。 ODS_SELECTEDは項目が選択状態になったときセットされます。(具体的にいうとマウスにポイントされて 選択状態になった、とういこと) 選択状態になったら、テキストの色を赤くして背景の色を青にしています。選択状態で ないときは、特に指定していないのでデフォルトの色になります。

RECT構造体rcは、描画すべき領域の境界を定義する長方形です。

RECT構造体については第5章で解説してあります。TextOutするときは この領域の左上からすればよいので

TextOut(hdc, rc.left, rc.top,....

としています。この領域からあふれて描画するとまずいことが起こります。 (MEASUREITEMSTRUCT構造体にセットしたものと違うものを描画したときなど) さて、いろいろ前置きが長くなってしまいました。 実際のプログラムは次章で作ることにします。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Jun/15/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。