所有者描画コンボ ボックスを作成する方法
このトピックでは、所有者描画コンボ ボックスを使用する方法について説明します。 この C++ コード例では、所有者描画ドロップダウン リスト ボックスを使用して、ビットマップと名前で表される 4 つの食品グループを表示します。 食品グループを選択すると、そのグループ内の食品がリスト表示されます。
ダイアログ ボックスには、リスト ボックス (IDLIST) と、[OK] (IDOK) および [キャンセル] (IDCANCEL) の 2 つのボタンも含まれます。 IDOK 定数と IDCANCEL 定数は、SDK ヘッダー ファイルによって定義されます。 IDLIST 定数はアプリケーションのヘッダー ファイルで定義されます。現状では、制御識別子 IDCOMBO です。 ダイアログ ボックスの詳細については、「ダイアログ ボックス」を参照してください。
知っておくべきこと
テクノロジ
前提条件
- C/C++
- Windows ユーザー インターフェイス プログラミング
手順
手順 1: 所有者描画ダイアログ ボックスを作成する
このコード例では、DialogBox 関数を使用して、モーダル ダイアログ ボックスを作成します。 ダイアログ ボックス テンプレート IDD_SQMEAL では、ウィンドウ スタイル、ボタン、およびコンボ ボックスのコントロール識別子が定義されています。 この例のコンボ ボックスでは、CBS_DROPDOWNLIST、CBS_OWNERDRAWFIXED、CBS_SORT、CBS_HASSTRINGS、WS_VSCROLL、WS_TABSTOP のスタイルを使用します。
DialogBox(hInst, MAKEINTRESOURCE(IDD_SQMEAL),
hWnd, FoodDlgProc);
手順 2: ダイアログ ボックスで WM_INITDIALOG メッセージと WM_DESTROY メッセージを処理します。
ダイアログ ボックスでコンボ ボックスを使用するときは、通常は、コンボ ボックスを初期化することで WM_INITDIALOG メッセージに応答します。 アプリケーションは、所有者描画コンボ ボックスに使用されるビットマップを読み込んでから、アプリケーション定義の InitGroupList
関数を呼び出して、コンボ ボックスを初期化します。 また、コンボ ボックス内の最初のリスト項目を選択してから、アプリケーション定義の InitFoodList
関数を呼び出して、リスト ボックスを初期化します。
この例での所有者描画コンボ ボックスは、4 つの食品グループそれぞれの名前を含むドロップダウン リスト ボックスです。 InitGroupList
は、各食品グループの名前を追加し、CB_SETITEMDATA メッセージを使用して、食品グループを識別する各リスト項目にビットマップのハンドルを関連付けます。
この例のリスト ボックスには、選択された食品グループ内の食品の名前が含まれます。 InitFoodList は、リスト ボックスの内容をリセットしてから、現在の食品グループ ドロップダウン リスト ボックスでの現在の食品の選択に応じた名前を追加します。
case WM_INITDIALOG:
// Call an application-defined function to load bitmap resources.
if (!LoadIconBitmaps())
{
EndDialog(hDlg, -1);
break;
}
// Initialize the food groups combo box and select the first item.
InitGroupList(hDlg);
SendDlgItemMessage(hDlg, IDCOMBO, CB_SETCURSEL, 0, 0);
// Initialize the food list box and select the first item.
InitFoodList(hDlg);
SendDlgItemMessage(hDlg, IDLIST, LB_SETCURSEL, 0, 0);
return (INT_PTR)TRUE;
WM_DESTROY メッセージを受信したら、アプリケーションは所有者描画コンボ ボックス内のビットマップを削除します。
case WM_DESTROY:
// Call the application-defined function to free the bitmap resources.
DeleteIconBitmaps();
break;
手順 3: WM_MEASUREITEM メッセージを処理します。
所有者描画コンボ ボックスは、親ウィンドウまたはダイアログ ボックス プロシージャに WM_MEASUREITEM メッセージを送信して、アプリケーションが各リスト項目の寸法を設定できるようにします。 この例のコンボ ボックスには CBS_OWNERDRAWFIXED スタイルが設定されているため、システムは WM_MEASUREITEM メッセージを一度だけ送信します。 CBS_OWNERDRAWVARIABLE スタイルが設定されたコンボ ボックスは、リスト項目ごとに WM_MEASUREITEM メッセージを送信します。
lParam パラメーターは、コントロールとリスト項目を識別する MEASUREITEMSTRUCT 構造体へのポインターです。 また、リスト項目の既定の寸法も含まれます。 この例では、itemHeight 構造体メンバーを変更して、リスト項目に食品グループのビットマップを表示するのに十分な高さが確保されるようにします。
case WM_MEASUREITEM:
{
// Set the height of the items in the food groups combo box.
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
if (lpmis->itemHeight < CY_BITMAP + 2)
lpmis->itemHeight = CY_BITMAP + 2;
break;
}
手順 4: WM_DRAWITEM メッセージを処理します。
所有者描画コンボ ボックスは、アプリケーションでリスト項目の再描画が必要になるたびに、親ウィンドウまたはダイアログ ボックス プロシージャに WM_DRAWITEM メッセージを送信します。 lParam パラメーターは、コントロールとリスト項目を識別する DRAWITEMSTRUCT 構造体へのポインターです。 また、項目を描画するために必要な情報も含まれています。
この例のアプリケーションでは、リスト項目のテキストと、食品グループに関連付けられているビットマップが表示されます。 項目にフォーカスがある場合は、フォーカスの四角形も描画します。 この例では、テキストを表示する前に、選択された項目に基づいて前景色と背景色を設定します。 コンボ ボックスに CBS_HASSTRINGS スタイルが設定されているため、CB_GETLBTEXT メッセージを使用して取得可能な各リスト項目について、コンボ ボックスはテキストを保持します。
リスト項目に使用されるビットマップは、食品グループによって異なります。 InitGroupList
は、CB_SETITEMDATA メッセージを使用して、各リスト項目にビットマップのハンドルを関連付けます。 ウィンドウ プロシージャは、DRAWITEMSTRUCT 構造体の itemData メンバーから、ビットマップのハンドルを取得します。 システムは、食品グループのシンボルごとに 2 つのビットマップを使用します。画像の背後にある不規則な領域を消去する SRCAND ラスター操作を含むモノクロ ビットマップと、画像を描画する SRCPAINT ラスター操作を含むカラー ビットマップです。
case WM_DRAWITEM:
{
COLORREF clrBackground;
COLORREF clrForeground;
TEXTMETRIC tm;
int x;
int y;
HRESULT hr;
size_t cch;
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam;
if (lpdis->itemID == -1) // Empty item)
break;
// Get the food icon from the item data.
hbmIcon = (HBITMAP) lpdis->itemData;
// The colors depend on whether the item is selected.
clrForeground = SetTextColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
clrBackground = SetBkColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHT : COLOR_WINDOW));
// Calculate the vertical and horizontal position.
GetTextMetrics(lpdis->hDC, &tm);
y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
x = LOWORD(GetDialogBaseUnits()) / 4;
// Get and display the text for the list item.
SendMessage(lpdis->hwndItem, CB_GETLBTEXT,
lpdis->itemID, (LPARAM) achTemp);
hr = StringCchLength(achTemp, 256, &cch);
if (FAILED(hr))
{
// TODO: Write error handler.
}
ExtTextOut(lpdis->hDC, CX_BITMAP + 2 * x, y,
ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,
achTemp, (UINT)cch, NULL);
// Restore the previous colors.
SetTextColor(lpdis->hDC, clrForeground);
SetBkColor(lpdis->hDC, clrBackground);
// Draw the food icon for the item.
HDC hdc = CreateCompatibleDC(lpdis->hDC);
if (hdc == NULL)
break;
SelectObject(hdc, hbmMask);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCAND);
SelectObject(hdc, hbmIcon);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCPAINT);
DeleteDC(hdc);
// If the item has the focus, draw the focus rectangle.
if (lpdis->itemState & ODS_FOCUS)
DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
break;
}
手順 5: WM_COMMAND メッセージを処理します。
ダイアログ ボックス コントロールでイベントが発生すると、コントロールは WM_COMMAND メッセージを使用してダイアログ ボックス プロシージャに通知します。 この例では、コンボ ボックス、リスト ボックス、[OK] ボタンからの通知メッセージを処理します。 コントロール識別子は、wParam の下位ワードに格納されます。また、通知コードは、wParam の上位ワードに格納されます。
コントロール識別子が IDCOMBO である場合、イベントはコンボ ボックスで発生したものです。 これに応答して、選択が行われたこと、ドロップダウン リストが閉じられたこと、変更を受け入れる必要があることを示すCBN_SELENDOK を除き、ダイアログ ボックス プロシージャはその他のすべてのコンボ ボックス イベントを無視します。 ダイアログ ボックス プロシージャは、InitFoodList
を呼び出して、リスト ボックスの内容をリセットし、ドロップダウン リスト ボックスでの現在の選択に応じた名前を追加します。
コントロール識別子が IDLIST である場合、イベントはリスト ボックスで発生したものです。 この場合、ユーザーがリスト項目をダブルクリックしたことを示す LBN_DBLCLK を除いて、ダイアログ ボックス プロシージャはすべてのリスト ボックス イベントを無視します。 このイベントは、[OK] ボタンがクリックされたのと同様に処理されます。
コントロール識別子が IDOK である場合、ユーザーが [OK] ボタンをクリックしました。 これに応答して、ダイアログ ボックス プロシージャは、選択された食品の名前をアプリケーションの複数行編集コントロールに挿入してから、EndDialog 関数を呼び出してダイアログ ボックスを閉じます。
コントロール識別子が IDCANCEL である場合、ユーザーが [キャンセル] ボタンをクリックしました。 それに応答して、ダイアログ ボックス プロシージャは、EndDialog を呼び出してダイアログ ボックスを閉じます。
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCOMBO:
if (HIWORD(wParam) == CBN_SELENDOK)
{
InitFoodList(hDlg);
SendDlgItemMessage(hDlg, IDLIST,
LB_SETCURSEL, 0, 0);
}
break;
case IDLIST:
if (HIWORD(wParam) != LBN_DBLCLK)
break;
// For a double-click, process the OK case.
case IDOK:
// Get the text for the selected list item.
hwnd = GetDlgItem(hDlg, IDLIST);
// Here it is assumed the text can fit into achTemp.
// If there is doubt, call LB_GETTEXTLENGTH first.
SendMessage(hwnd, LB_GETTEXT,
SendMessage(hwnd, LB_GETCURSEL, 0, 0),
(LPARAM) achTemp);
// TODO: Do something with the selected text.
EndDialog(hDlg, 0);
break;
case IDCANCEL:
hwnd = GetDlgItem(hDlg, IDCOMBO);
if (SendMessage(hwnd, CB_GETDROPPEDSTATE, 0, 0))
SendMessage(hwnd, CB_SHOWDROPDOWN, FALSE, 0);
else EndDialog(hDlg, 0);
}
break;
コード例全体
以下に、Square Meal ダイアログ ボックスのダイアログ ボックス プロシージャと補助的な関数を示します。
#define ID_BREAD 0
#define ID_DAIRY 1
#define ID_FRUIT 2
#define ID_MEAT 3
#define CX_BITMAP 24
#define CY_BITMAP 24
HBITMAP hbmBread;
HBITMAP hbmDairy;
HBITMAP hbmMeat;
HBITMAP hbmFruit;
HBITMAP hbmMask;
HBITMAP hbmIcon;
// Message handler for Square Meal dialog box.
INT_PTR CALLBACK FoodDlgProc(HWND hDlg, UINT message, WPARAM wParam,
LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
TCHAR achTemp[256];
HWND hwnd;
switch (message)
{
case WM_INITDIALOG:
// Call an application-defined function to load bitmap resources.
if (!LoadIconBitmaps())
{
EndDialog(hDlg, -1);
break;
}
// Initialize the food groups combo box and select the first item.
InitGroupList(hDlg);
SendDlgItemMessage(hDlg, IDCOMBO, CB_SETCURSEL, 0, 0);
// Initialize the food list box and select the first item.
InitFoodList(hDlg);
SendDlgItemMessage(hDlg, IDLIST, LB_SETCURSEL, 0, 0);
return (INT_PTR)TRUE;
case WM_MEASUREITEM:
{
// Set the height of the items in the food groups combo box.
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
if (lpmis->itemHeight < CY_BITMAP + 2)
lpmis->itemHeight = CY_BITMAP + 2;
break;
}
case WM_DRAWITEM:
{
COLORREF clrBackground;
COLORREF clrForeground;
TEXTMETRIC tm;
int x;
int y;
HRESULT hr;
size_t cch;
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam;
if (lpdis->itemID == -1) // Empty item)
break;
// Get the food icon from the item data.
hbmIcon = (HBITMAP) lpdis->itemData;
// The colors depend on whether the item is selected.
clrForeground = SetTextColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
clrBackground = SetBkColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHT : COLOR_WINDOW));
// Calculate the vertical and horizontal position.
GetTextMetrics(lpdis->hDC, &tm);
y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
x = LOWORD(GetDialogBaseUnits()) / 4;
// Get and display the text for the list item.
SendMessage(lpdis->hwndItem, CB_GETLBTEXT,
lpdis->itemID, (LPARAM) achTemp);
hr = StringCchLength(achTemp, 256, &cch);
if (FAILED(hr))
{
// TODO: Write error handler.
}
ExtTextOut(lpdis->hDC, CX_BITMAP + 2 * x, y,
ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,
achTemp, (UINT)cch, NULL);
// Restore the previous colors.
SetTextColor(lpdis->hDC, clrForeground);
SetBkColor(lpdis->hDC, clrBackground);
// Draw the food icon for the item.
HDC hdc = CreateCompatibleDC(lpdis->hDC);
if (hdc == NULL)
break;
SelectObject(hdc, hbmMask);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCAND);
SelectObject(hdc, hbmIcon);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCPAINT);
DeleteDC(hdc);
// If the item has the focus, draw the focus rectangle.
if (lpdis->itemState & ODS_FOCUS)
DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
break;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCOMBO:
if (HIWORD(wParam) == CBN_SELENDOK)
{
InitFoodList(hDlg);
SendDlgItemMessage(hDlg, IDLIST,
LB_SETCURSEL, 0, 0);
}
break;
case IDLIST:
if (HIWORD(wParam) != LBN_DBLCLK)
break;
// For a double-click, process the OK case.
case IDOK:
// Get the text for the selected list item.
hwnd = GetDlgItem(hDlg, IDLIST);
// Here it is assumed the text can fit into achTemp.
// If there is doubt, call LB_GETTEXTLENGTH first.
SendMessage(hwnd, LB_GETTEXT,
SendMessage(hwnd, LB_GETCURSEL, 0, 0),
(LPARAM) achTemp);
// TODO: Do something with the selected text.
EndDialog(hDlg, 0);
break;
case IDCANCEL:
hwnd = GetDlgItem(hDlg, IDCOMBO);
if (SendMessage(hwnd, CB_GETDROPPEDSTATE, 0, 0))
SendMessage(hwnd, CB_SHOWDROPDOWN, FALSE, 0);
else EndDialog(hDlg, 0);
}
break;
case WM_DESTROY:
// Call the application-defined function to free the bitmap resources.
DeleteIconBitmaps();
break;
}
return (INT_PTR)FALSE;
}
// Loads string resources and adds them as items to the drop-down list of
// the food groups combo box. The bitmap handle of each item's icon is
// stored as item data for easy access when the item needs to be drawn.
//
void InitGroupList(HWND hDlg)
{
TCHAR achTemp[256];
DWORD dwIndex;
// Get the handle of the food groups combo box.
HWND hwndGroupsBox = GetDlgItem(hDlg, IDCOMBO);
LoadString(hInst, IDS_BREAD, achTemp, sizeof(achTemp)/sizeof(TCHAR));
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM) achTemp);
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM) hbmBread);
LoadString(hInst, IDS_DAIRY, achTemp, sizeof(achTemp)/sizeof(TCHAR));
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM) achTemp);
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM) hbmDairy);
LoadString(hInst, IDS_FRUIT, achTemp, sizeof(achTemp)/sizeof(TCHAR));
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM) achTemp);
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM) hbmFruit);
LoadString(hInst, IDS_MEAT, achTemp, sizeof(achTemp)/sizeof(TCHAR));
dwIndex = SendMessage(hwndGroupsBox, CB_ADDSTRING, 0, (LPARAM) achTemp);
SendMessage(hwndGroupsBox, CB_SETITEMDATA, dwIndex, (LPARAM) hbmMeat);
return;
}
// Fills the food list based on the selected item in the food groups
// combo box.
void InitFoodList(HWND hDlg)
{
TCHAR achTemp[256];
HWND hwndGroupsBox = GetDlgItem(hDlg, IDCOMBO);
HWND hwndFoodList = GetDlgItem(hDlg, IDLIST);
// Clear the list contents.
SendMessage(hwndFoodList, LB_RESETCONTENT, 0, 0);
// Find out which food group is selected.
int idFoodGroup = SendMessage(hwndGroupsBox, CB_GETCURSEL, 0, 0);
switch (idFoodGroup)
{
case ID_BREAD:
LoadString(hInst, IDS_OAT, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_WHEAT, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_RYE, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
break;
case ID_DAIRY:
LoadString(hInst, IDS_CHEDDAR, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_MILK, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_PROCESSED, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_SWISS, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
break;
case ID_FRUIT:
LoadString(hInst, IDS_APPLES, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_BANANAS, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_ORANGES, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
break;
case ID_MEAT:
LoadString(hInst, IDS_BEEF, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_CHICKEN, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
LoadString(hInst, IDS_PORK, achTemp, sizeof(achTemp)/sizeof(TCHAR));
SendMessage(hwndFoodList, LB_ADDSTRING, 0, (LPARAM) achTemp);
break;
default:
break;
}
return;
}
// Loads the food icon bitmaps from the application resources.
//
BOOL LoadIconBitmaps(void)
{
hbmBread = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BREAD));
if (hbmBread != NULL)
hbmDairy = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_DAIRY));
if (hbmDairy != NULL)
hbmMeat = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MEAT));
if (hbmMeat != NULL)
hbmFruit = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FRUIT));
if (hbmFruit != NULL)
hbmMask = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MASK));
if (hbmMask != NULL)
return TRUE;
return FALSE;
}
// Frees the icon bitmps.
//
void DeleteIconBitmaps(void)
{
FreeResource(reinterpret_cast<HGLOBAL>(hbmBread));
FreeResource(reinterpret_cast<HGLOBAL>(hbmDairy));
FreeResource(reinterpret_cast<HGLOBAL>(hbmMeat));
FreeResource(reinterpret_cast<HGLOBAL>(hbmFruit));
FreeResource(reinterpret_cast<HGLOBAL>(hbmMask));
}
関連トピック