第196章 拡張コンボボックスと通知メッセージ2


今回は、拡張コンボボックスの新しい通知メッセージCBEN_GETDISPINFO の処理のし方を解説します。 これは、コールバックアイテムの表示情報を 取得するために送られてきます。また、従来のコンボボックス通知メッセージとは 異なりWM_NOTIFYの形で送られてきます。他の新しい通知メッセージ(CBEN_*)も すべてWM_NOTIFYの形で送られてきます。



見た目は、今まで作ったコンボボックスと同じです。 しかし、プログラム方法がかなり異なります。



では、必要なメッセージやら、構造体などを見てみましょう。

CBEN_GETDISPINFO pDispInfo = (PNMCOMBOBOXEX) lParam;

pDispInfoはNMCOMBOBOXEX構造体へのポインタです。 この構造体は、

typedef struct { NMHDR hdr; COMBOBOXEXITEM ceItem; } NMCOMBOBOXEX, *PNMCOMBOBOXEX;

のように定義されています。
hdrは毎度おなじみのNMHDR構造体です。
ceItemメンバは現在の通知のアイテム情報を持っています。 通知メッセージを受け取ったらこの構造体はオーナーの 応答を求めています。(この構造体を設定すればコールバックアイテムが 描画される)

さて、コールバックアイテムはどのように設定すればよいのでしょうか。 これは簡単です。CBEM_INSERTメッセージでアイテムを挿入する時 COMBOBOXEXITEM構造体のメンバを次のように設定することで実現できます。

メンバコールバック値
pszTextLPSTR_TEXTCALLBACK
iImageI_IMAGECALLBACK
iSelectedImageI_IMAGECALLBACK
iOverlayI_IMAGECALLBACK
iIndentI_INDENTCALLBACK

コールバックアイテムの表示法をまとめると

1.CBEM_INSERTITEMメッセージでアイテムを挿入時   COMBOBOXEXITEM構造体のメンバにコールバック値を設定する 2.CBEN_GETDISPINFO通知メッセージを処理

ということになります。
さて、これだけではアイテム表示が必要な時(ドロップダウンリストが 出てくる時など)にいつも通知メッセージがやってきて非効率的です。 それで、CBEN_GETDISPINFO通知メッセージが来た時に COMBOBOXEXITEM構造体のmaskメンバにCBEIF_DI_SETITEMを加えてやります。 これで、この通知メッセージは初回しかきません。データはコントロールが 保持します。状況に応じて表示するアイテムを変えたい時は、CBEIF_DI_SETITEMを加えては いけません。

では、プログラムを見てみましょう。

ビットマップは前章と全く同じものを使っています。

// comboex3.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP BITMAP DISCARDABLE "mybmp.bmp"

リソース・スクリプトはこれだけです。

// comboex3.cpp #ifndef STRICT #define STRICT #endif #define ID_COMBO 100 #include <windows.h> #include <commctrl.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void SetMyComboEx(HWND hCombo); char szClassName[] = "comboex3"; //ウィンドウクラス HINSTANCE hInst;

今回はプログラムを簡略化するためインスタンスハンドルを 保存するグローバル変数を用意しました。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかる拡張コンボボックス", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

このへんはいつもと同じです。WinMain関数でインスタンスハンドルを グローバル変数にコピーしています。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; INITCOMMONCONTROLSEX ic; static HWND hCombo; static HFONT hFont; static HIMAGELIST hImg; LPNMHDR lpn; PNMCOMBOBOXEX pncombo; switch (msg) { case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_USEREX_CLASSES; InitCommonControlsEx(&ic); hCombo = CreateWindowEx(0, WC_COMBOBOXEX, "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, 10, 10, 150, 200, hWnd, (HMENU)ID_COMBO, hInst, NULL); hFont = CreateFont(20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "DF勘亭流"); SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont, 0); hImg = ImageList_LoadBitmap(hInst, "MYBMP", 16, 15, RGB(255, 0, 0)); SendMessage(hCombo, CBEM_SETIMAGELIST, 0, (LPARAM)hImg); SetMyComboEx(hCombo); break; case WM_NOTIFY: lpn = (LPNMHDR)lp; if (lpn->hwndFrom == hCombo && lpn->code == CBEN_GETDISPINFO) { pncombo = (PNMCOMBOBOXEX)lp; pncombo->ceItem.mask |= CBEIF_DI_SETITEM; //MessageBox(hWnd, "CBEN_GETDISPINFOが来た", "OK", MB_OK); //ここでMessageBoxを実行すると最初にコンボボックスを開く時のみ //アイテムの数だけメッセージボックスが出てくる //その後は出てこない。もしCBEIF_DI_SETITEMを設定しないと //ずっとメッセージボックスが出てドロップダウンメニューを開けない switch (pncombo->ceItem.iItem) { case 0: pncombo->ceItem.pszText = "丸"; pncombo->ceItem.iImage = 0; pncombo->ceItem.iSelectedImage = 0; pncombo->ceItem.iIndent = 0; break; case 1: pncombo->ceItem.pszText = "三角"; pncombo->ceItem.iImage = 1; pncombo->ceItem.iSelectedImage = 1; pncombo->ceItem.iIndent = 1; break; case 2: pncombo->ceItem.pszText = "バツ"; pncombo->ceItem.iImage = 2; pncombo->ceItem.iSelectedImage = 2; pncombo->ceItem.iIndent = 2; break; case 3: pncombo->ceItem.pszText = "丸2"; pncombo->ceItem.iImage = 0; pncombo->ceItem.iSelectedImage = 0; pncombo->ceItem.iIndent = 0; break; case 4: pncombo->ceItem.pszText = "三角2"; pncombo->ceItem.iImage = 1; pncombo->ceItem.iSelectedImage = 1; pncombo->ceItem.iIndent = 1; break; case 5: pncombo->ceItem.pszText = "バツ2"; pncombo->ceItem.iImage = 2; pncombo->ceItem.iSelectedImage = 2; pncombo->ceItem.iIndent = 2; break; } } else return DefWindowProc(hWnd, msg, wp,lp); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DeleteObject(hFont); ImageList_Destroy(hImg); DestroyWindow(hCombo); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

今回はプログラムを簡略化するため親ウィンドウ(メインウィンドウ)のクライアント 領域に直接拡張コンボボックスを表示しています。

WM_CREATEが来たらコモンコントロールを初期化してコンボボックスを作ります。 また、WM_SETFONTメッセージでコンボボックスにフォントを設定しています。

CBEN_GETDISPINFO通知メッセージが来たらCOMBOBOXEXITEM構造体のコールバック値が 設定されているメンバを記述します。コメントにも書いてありますがこの通知 メッセージを受け取った時にmaskメンバにCBEIF_DI_SETITEMを加えておくと 初回しかメッセージが来ません。

プログラム終了時にフォントやら、イメージリストの破棄を行います。

void SetMyComboEx(HWND hCombo) { COMBOBOXEXITEM ci; memset(&ci, 0, sizeof(COMBOBOXEXITEM)); ci.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT; //注 ここでCBEIF_DI_SETITEMを設定してもだめです。 ci.pszText = LPSTR_TEXTCALLBACK; ci.iItem = 0; ci.iIndent = I_INDENTCALLBACK; ci.iImage = I_IMAGECALLBACK; ci.iSelectedImage = I_IMAGECALLBACK; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.iItem = 1; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.iItem = 2; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.iItem = 3; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.iItem = 4; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.iItem = 5; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); return; }

拡張コンボボックスにアイテムを挿入する関数です。

筆者は間違ってここでCOMBOBOXEXITEM構造体のmaskメンバに CBEIF_DI_SETITEMを設定していました。これだと、しょっちゅう CBEN_GETDISPINFO通知メッセージが発行されてしまいます。

今回はわかってしまうとさほど難しくないのですが、ちょっとした事で つまずくとバグを発見しにくいプログラムとなります。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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