Hur man skapar en Owner-Drawn kombinationsruta
Det här avsnittet visar hur du använder en ägarritad kombinationsruta. I C++-kodexemplet används en ägarritad listruta för att visa de fyra matgrupperna, vardera representerad av en bitmapp och ett namn. Om du väljer en livsmedelsgrupp visas livsmedel i den gruppen i en lista.
Dialogrutan innehåller också en listruta (IDLIST) och två knappar: OK (IDOK) och Avbryt (IDCANCEL). Konstanterna IDOK och IDCANCEL definieras av SDK-huvudfilerna. Den konstanta IDLIST definieras i programmets huvudfil, liksom kontrollidentifieraren IDCOMBO. Mer information om dialogrutor finns i dialogrutor.
Vad du behöver veta
Teknologier
Förutsättningar
- C/C++
- Programmering av Windows-användargränssnitt
Instruktioner
Steg 1: Skapa dialogrutan Owner-Drawn
Kodexemplet använder funktionen DialogBox för att skapa en modal dialogruta. Dialogrutans mall, IDD_SQMEAL, definierar fönsterformat, knappar och kontrollidentifierare för kombinationsrutan. Kombinationsrutan i det här exemplet använder stilarna CBS_DROPDOWNLIST, CBS_OWNERDRAWFIXED, CBS_SORT, CBS_HASSTRINGS, WS_VSCROLLoch WS_TABSTOP.
DialogBox(hInst, MAKEINTRESOURCE(IDD_SQMEAL),
hWnd, FoodDlgProc);
Steg 2: Bearbeta WM_INITDIALOG och WM_DESTROY meddelanden i en dialogruta.
När du använder en kombinationsruta i en dialogruta svarar du vanligtvis på ett WM_INITDIALOG meddelande genom att initiera kombinationsrutan. Programmet laddar de bitmappar som används för den ägardefinierade kombinationsrutan och anropar sedan den programdefinierade funktionen InitGroupList
för att initiera kombinationsrutan. Den väljer också det första listobjektet i kombinationsrutan och anropar sedan den programdefinierade funktionen InitFoodList
för att initiera listrutan.
I exemplet är den ägarritade kombinationsrutan en nedrullningsbar listruta som innehåller de fyra matgruppernas namn.
InitGroupList
lägger till namnet på varje matgrupp och använder meddelandet CB_SETITEMDATA för att associera en bitmappsreferens med varje listobjekt som identifierar en matgrupp.
Listrutan i exemplet innehåller namnen på livsmedel i den valda livsmedelsgruppen. InitFoodList återställer innehållet i listrutan och lägger sedan till namnen på det aktuella matvalet i listrutan för den aktuella matgruppen.
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;
När programmet mottar meddelandet WM_DESTROY tar det bort bitmapparna i kombinationsrutan med ägarteckning.
case WM_DESTROY:
// Call the application-defined function to free the bitmap resources.
DeleteIconBitmaps();
break;
Steg 3: Bearbeta meddelandet WM_MEASUREITEM.
En ägarritad kombinationsruta skickar meddelandet WM_MEASUREITEM till det överordnade fönstret eller dialogrutan så att programmet kan ange dimensionerna för varje listobjekt. Eftersom exempelkombinationsrutan har CBS_OWNERDRAWFIXED stil skickar systemet endast WM_MEASUREITEM meddelandet en gång. Kombinationsrutor med stilen CBS_OWNERDRAWVARIABLE skickar ett WM_MEASUREITEM meddelande för varje listelement.
Parametern lParam är en pekare till en MEASUREITEMSTRUCT struktur som identifierar kontroll- och listobjektet. Den innehåller också standarddimensionerna för listobjektet. I exemplet ändras itemHeight strukturmedlem för att säkerställa att listobjekten är tillräckligt höga för att rymma bitmapparna för livsmedelsgruppen.
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;
}
Steg 4: Bearbeta meddelandet WM_DRAWITEM.
En ägardefinierad kombinationsruta skickar WM_DRAWITEM meddelandet till det överordnade fönstret eller dialogruteproceduren varje gång programmet måste måla om ett listelement. Parametern lParam är en pekare till en DRAWITEMSTRUCT struktur som identifierar kontroll- och listobjektet. Den innehåller också information som behövs för att måla objektet.
Exempelprogrammet visar listobjekttexten och bitmappen som är associerad med matgruppen. Om objektet har fokus ritas även en fokusrektangel. Innan du visar texten anger exemplet förgrunds- och bakgrundsfärgerna baserat på det markerade objektet. Eftersom kombinationsrutan har CBS_HASSTRINGS stil behåller kombinationsrutan texten för varje listobjekt som kan hämtas genom CB_GETLBTEXT meddelandet.
Vilka bitmappar som används för listobjektet beror på matgruppen.
InitGroupList
använder meddelandet CB_SETITEMDATA för att associera ett bitmappshandtag med varje listobjekt. Fönsterproceduren hämtar bitmappshandtaget från itemData medlem i DRAWITEMSTRUCT- struktur. Systemet använder två bitmappar för varje matgruppssymbol: en monokrom bitmapp med SRCAND-rasteråtgärden för att radera den oregelbundna regionen bakom bilden och en färgbitmapp med SRCPAINT-rasteråtgärden för att måla bilden.
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;
}
Steg 5: Bearbeta meddelandet WM_COMMAND.
När en händelse inträffar i en dialogrutekontroll meddelar kontrollen dialogruteproceduren med hjälp av ett WM_COMMAND meddelande. Exemplet bearbetar meddelanden från kombinationsrutan, listrutan och knappen OK. Kontrollidentifieraren finns i lågordningsordet i wParam, och meddelandekoden är i högordningsordet i wParam.
Om kontrollidentifieraren är IDCOMBO har en händelse inträffat i kombinationsrutan. Som svar ignorerar dialogruteproceduren alla andra kombinationsrutehändelser förutom CBN_SELENDOK, vilket indikerar att ett val gjordes, listrutan stängdes och att de ändringar som gjorts bör godkännas. Dialogrutan anropar InitFoodList
för att återställa innehållet i listrutan och lägga till namnen på de aktuella valen i rullgardinsmenyn.
Om kontrollidentifieraren är IDLIST har en händelse inträffat i listrutan. Detta gör att dialogruteproceduren ignorerar alla listrutehändelser förutom LBN_DBLCLK, vilket indikerar att användaren har dubbelklickat på ett listobjekt. Den här händelsen bearbetas på samma sätt som om en OK-knappen hade valts.
Om kontrollidentifieraren är IDOK har användaren valt knappen OK. Som svar infogar dialogruteproceduren namnet på den valda maten i programmets flerradsredigeringskontroll och anropar sedan funktionen EndDialog för att stänga dialogrutan.
Om kontrollidentifieraren är IDCANCEL har användaren klickat på knappen Avbryt. Som svar anropar dialogruteproceduren EndDialog för att stänga dialogrutan.
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;
Fullständigt exempel
Följande är dialogrutans procedur och stödfunktionerna för dialogrutan 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));
}
Relaterade ämnen