今回作るプログラムのダイアログボックスは左の図のような
感じになります。イメージは選択されていてもいなくても同じです。
まず、左の図のようなビットマップ(mybmp.bmp)を用意します。
ひとつのイメージは16*15にしてあります。マスクしたいところを
「赤,RGB(255, 0, 0)」で塗りつぶしてあります。
では、プログラムをみてみましょう。
今回は拡張コンボボックスをリソース・エディタに作らせています。 ダイアログボックスのフォントの大きさは11ポイントにしておいて下さい。 (デフォルトでは9ポイント。これではイメージの一部がコンボボックスの エディットコントロール部に表示されない。) 先ほどのmybmp.bmpは"MYBMP"という名前のリソースにしています。// comboex2.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "ダイアログを出す(&D)", IDM_DLG END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 115, 49 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "拡張コンボボックス" FONT 11, "MS Pゴシック" BEGIN DEFPUSHBUTTON "閉じる",IDOK,32,28,50,14 CONTROL "",IDC_COMBOBOXEX1,"ComboBoxEx32",CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP,7,7,101,44 END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP BITMAP DISCARDABLE "mybmp.bmp"
いつもと似ていますが、グローバル変数がひとつもないことに注意して下さい。// comboex2.cpp #ifndef STRICT #define STRICT #endif #define WM_MYMSG WM_USER #include <windows.h> #include <commctrl.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void SetMyComboEx(HWND); typedef struct { HWND hWnd; HINSTANCE hInst; } MYDATA;
また自作メッセージWM_MYMSGを定義している点に注意して下さい。 さらに、自作のMYDATA型を定義している点にも注意して下さい。
これは、いつもと同じです。int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
lpszClassNameメンバのところがいつもと違います。//ウィンドウ・クラスの登録 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 = "MYMENU"; //メニュー名 wc.lpszClassName = "comboex2"; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); }
CreateWindow関数の第1引数はInitApp関数の所で使ったクラス名と同じにしください。//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow("comboex2", "猫でもわかる拡張コンボボックス", //タイトルバーにこの名前が表示されます 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; }
自作メッセージ(WM_MYMSG)が来たら、strにlParamの内容をコピーしています。 この自作メッセージはダイアログボックスのプロシージャで拡張コンボボックスの CBN_SELCHANGE通知メッセージを捕まえた時に発信されます(後述)。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; INITCOMMONCONTROLSEX ic; LPCREATESTRUCT pcs; static MYDATA md; HDC hdc; PAINTSTRUCT ps; static TCHAR str[256]; switch (msg) { case WM_MYMSG: wsprintf(str, "「%s」が選択されています。", (LPTSTR)lp); break; case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_USEREX_CLASSES; InitCommonControlsEx(&ic); pcs = (LPCREATESTRUCT)lp; md.hInst = pcs->hInstance; md.hWnd = hWnd; break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); TextOut(hdc, 10, 10, str, strlen(str)); EndPaint(hWnd, &ps); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_DLG: DialogBoxParam(md.hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc, (LPARAM)&md); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
WM_CREATEメッセージが来たら、コモンコントロールを初期化します。
次にCREATESTRUCT構造体から現在のインスタンスハンドルを取得します。
この方法はすでに解説してあるので、
忘れた人は第19章を参照して下さい。
次に自作データ型MYDATAの変数mdに親ウィンドウのハンドルと、インスタンス ハンドルをセットします。この構造体のアドレスをあとでダイアログボックスに 渡します。(こんなことをしなくてもダイアログボックスのプロシージャで 取得できるのですが、今回はわざわざめんどうな方法を使っています。)
WM_PAINTメッセージが来たらクライアント領域にstrの内容を描画します。
メニューからIDM_DLG(「ダイアログを出す」)が選択されたら DialogBoxParam関数でダイアログボックスを出します。
DialogBoxマクロとほとんど同じですが、引数がひとつ多いです。int DialogBoxParam( HINSTANCE hInstance, // アプリケーションのインスタンス LPCTSTR lpTemplateName, // ダイアログボックステンプレートの名前 HWND hWndParent, // 親ウィンドウ DLGPROC lpDialogFunc, // ダイアログボックスのプロシージャのアドレス LPARAM dwInitParam // ダイアログボックスに渡すデータ );
dwInitParamはlpDialogFuncに送られてきたWM_INITDIALOGメッセージ のlParamとなります。
WM_INITDIALOGが来たら、lParamよりインスタンスハンドルと 親ウィンドウのハンドルを取得します。このデータは DialogBoxParam関数から送られてきたものです。// ダイアログボックスのプロシージャ LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static int nSelected; static HWND hCombo, hParent; static HIMAGELIST hImg; HINSTANCE hInst; static MYDATA *pmd; COMBOBOXEXITEM ci; TCHAR szText[256]; switch (msg) { case WM_INITDIALOG: pmd = (MYDATA *)lp; hInst = pmd->hInst; hParent = pmd->hWnd; hCombo = GetDlgItem(hDlg, IDC_COMBOBOXEX1); hImg = ImageList_LoadBitmap(hInst, "MYBMP", 16, 15, RGB(255, 0, 0)); SendMessage(hCombo, CBEM_SETIMAGELIST, 0, (LPARAM)hImg); SetMyComboEx(hCombo); SendMessage(hCombo, CB_SETCURSEL, (WPARAM)nSelected, 0); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: case IDCANCEL: nSelected = SendMessage(hCombo, CB_GETCURSEL, 0, 0); ImageList_Destroy(hImg); EndDialog(hDlg, IDOK); return TRUE; case IDC_COMBOBOXEX1: switch (HIWORD(wp)) { case CBN_SELCHANGE: ci.mask = CBEIF_TEXT; ci.pszText = szText; ci.cchTextMax = sizeof(szText); ci.iItem = -1; SendMessage(hCombo, CBEM_GETITEM, 0, (LPARAM)&ci); SendMessage(hParent, WM_MYMSG, 0, (LPARAM)szText); InvalidateRect(hParent, NULL, TRUE); return TRUE; } return FALSE; } return FALSE; } return FALSE; }
次にダイアログボックス上の拡張コンボボックスの ハンドルを取得します。
ImageList_LoadBitmapマクロの最後の引数はRGB(255, 0, 0)にして下さい。 今回は赤色でマスクしているのでこうなります。
次にCB_SETCURSELメッセージで拡張コンボボックスのエディット部分に 選択されているアイテムを表示されます。まだ、何も選択されていない 場合はnSelectedが0になっているので、最初のアイテムが表示されます。
「閉じる」(IDOK)ボタンか、「バッテン印」(IDCANCEL)が押された時は CB_GETCURSELメッセージを使って選択されているアイテムのインデックスを 取得してnSelectedにコピーしておきます。
コンボボックスのCBN_SELCHANGE通知メッセージは選択が変化した時 WM_COMMANDの形でやってきます。 (すでに第183章で使用しています。) WM_COMMANDのwParamのLOWORDはコントロールのID を示しています。また、wParamのHIWORDは通知メッセージです。
さて、CBN_SELCHANGE通知メッセージを捕まえたら、コンボボックスの アイテムを取得する必要があります。これは、COMBOBOXEXITEM構造体に 必要なメンバをセットしてCBEM_GETITEMメッセージを送ることことに より実現します。この場合必要なのはテキストのみなので COMBOBOXEXITEM構造体のmaskメンバはCBEIF_TEXTとなります。 取得したテキストを格納するバッファのアドレスをpszTextメンバに セットします。また、この場合cchTextMaxメンバを設定しなくては いけません。(拡張コンボボックスにアイテムを挿入する時は不要。) エディットコントロールのアイテムを取得する時はiItemメンバを−1に します。
また、この時親ウィンドウに自作メッセージ(WM_MYMSG)を送ります。 lParamに取得した文字列のアドレスを指定します。これで、親ウィンドウは 新しい文字列を知ることができます。さらに、InvalidateRect関数で 親ウィンドウにWM_PAINTメッセージを送ってクライアント領域を 再描画させます。
拡張コンボボックスにアイテムを挿入する関数です。 特に説明は不要ですね。void SetMyComboEx(HWND hCombo) { COMBOBOXEXITEM ci; memset(&ci, 0, sizeof(COMBOBOXEXITEM)); ci.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT; ci.pszText = "丸"; ci.iItem = 0; ci.iIndent = 0; ci.iImage = 0; ci.iSelectedImage = 0; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.pszText = "三角"; ci.iItem = 1; ci.iIndent = 1; ci.iImage = 1; ci.iSelectedImage = 1; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.pszText = "バツ"; ci.iItem = 2; ci.iIndent = 2; ci.iImage = 2; ci.iSelectedImage = 2; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.pszText = "丸2"; ci.iItem = 3; ci.iIndent = 0; ci.iImage = 0; ci.iSelectedImage = 0; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.pszText = "三角2"; ci.iItem = 4; ci.iIndent = 1; ci.iImage = 1; ci.iSelectedImage = 1; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); ci.pszText = "バツ2"; ci.iItem = 5; ci.iIndent = 2; ci.iImage = 2; ci.iSelectedImage = 2; SendMessage(hCombo, CBEM_INSERTITEM, 0, (LPARAM)&ci); return; }
今回はちょっとめんどうなことをやりました。 ウィンドウ間のデータやメッセージのやり取りを工夫するのは SDKプログラミングの最も面白いところです。
Update 17/Apr/1999 By Y.Kumei