これは、コントロール(ボタンとか、メニューとか・・)が作成されたときに オーナーに送られます。オーナーは、lParamで示された、 MEASUREITEMSTRUCT構造体に必要な情報を格納します。 メニューから送られてきたときはwParamは0です。 では、MEASUREITEMSTRUCT構造体とはどんなものでしょうか。WM_MEASUREITEM idCtl = (UINT) wParam; // コントロール識別子 lpmis = (LPMEASUREITEMSTRUCT) lParam; // アイテムの大きさ情報
要するに、WM_MEASUTREITEMメッセージが送られてきたら、lParam を調べてMEASUREITEMSTRUCT構造体にデータを入れてやります。 このときオーナードローするものがいっぱいあるときは、 switch文でコントロールタイプ別に種分けします。 (ODT_MENU, ODT_LISTBOX, ODT_BUTTON, ODT_COMBOBOX) つぎに、メニューならメニューアイテムID別に種分けをして アイテムの高さ、幅を代入してやります。オーナードローするものが 1種類だけならこの種分けの作業は不要です。 また、メニューの場合メニューアイテム識別子ごとに幅、高さを 設定しても一番大きいものにあわせられます。 (筆者のやり方がまずい?たとえばメューアイテムごとにフォントの 大きさを変えても、表示される領域は一番大きいフォントの高さに合わせられてしまう。) 最後のアイテムデータとは何でしょうか。VC++5.0のヘルプにはtypedef struct tagMEASUREITEMSTRUCT { // mis UINT CtlType; // コントロールタイプ UINT CtlID; // コンボボックス・リストボックス・ボタン識別子 UINT itemID; // メニューアイテム・可変高さのリストボックス・ // コンボボックス識別子 UINT itemWidth; // メニューアイテムの幅 UINT itemHeight; // メニューアイテムの高さ DWORD itemData; // アイテムデータ } MEASUREITEMSTRUCT;
Specifies the application-defined 32-bit value associated with the menu item
と書いてあります。 簡単に言ってしまうとAppendMenu関数の最後の引数で使ったものです。 ふつうは、最後の引数はメニューアイテムの名前ですが、オーナードローの場合 AppendMenu関数の段階ではまだメニューアイテムを表示しません。 それでは、ここまででどんな感じで使うのか例を示します。
親ウィンドウが作られたらすぐに、CreatePopupMenuを呼び出します。 (オーナードローでは第1階層のメニューはできない) そして、メニュー項目をAppendMenuで足していきます。 通常は、最後の引数は"終了"とか、"オプション"などといった 文字列が来ますね。ここでは、MENU_ENDとか、MENU_DOGなどという 列挙子を使っています。これがメニューデータになります。 次に、WM_MEASUREITEMメッセージのところでMEASUREITEMSTRUCT構造体に 幅と高さのデータを渡します。ここで使っているGetTextExtentPoint32関数は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;
となっています。これでSIZE構造体にこの文字列が要する 大きさが格納されます。なお16ビット版ではこの関数は 使えません。代わりにGetTextExtent関数というのがあります。BOOL GetTextExtentPoint32( HDC hdc, // デバイスコンテキストハンドル LPCTSTR lpString, // 文字列のアドレス int cbString, // 文字数 LPSIZE lpSize // SIZE構造体のアドレス );
これは、使い方が32ビット版とは少し違います。 戻り値の下位ワード(LOWORD)が幅を、上位ワード(HIWORD)が高さを表します。DWORD GetTextExtent(hdc, lpszString, cbString) HDC hdc; /* デバイス コンテキストのハンドル */ LPCSTR lpszString; /* 文字列のアドレス */ int cbString; /* 文字列内のバイト数 */
どちらの関数も場合によっては大きさに誤差の出ることがあります。
ところで、SIZE構造体についてはまだ解説をしていませんでした。
のように定義されています。 16ビット版では、typedef struct tagSIZE { // siz LONG cx; LONG cy; } SIZE;
のように定義されています。typedef struct tagSIZE { int cx; int cy; } SIZE;
なお、上のプログラム例でSetMyFontというのは、
自作関数でフォントを設定して、戻り値として
今までのフォントハンドルを返すものです。
さて、次にWM_DRAWITEMメッセージが来たときに、
メニューを書けばよいのですが具体例を見てみましょう。
古いCコンパイラではrc = lpDI->rcItem;がエラーになることがあります。 昔は(K&Rの時代)構造体を丸ごとコピーすることはできませんでした。 K&Rが「将来は構造体丸ごとの代入ができるようになる」と予言したそうです。RECT rc; 途中略 case WM_DRAWITEM: lpDI = (LPDRAWITEMSTRUCT)lp; rc = lpDI->rcItem; hdc = lpDI->hDC; hFont = SetMyFont("MS 明朝", 30); hFontOld = (HFONT)SelectObject(hdc, hFont); 以下略
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