Creating an Owner-Drawn List Box
The following example shows how to draw a list box that contains five owner-drawn items: four drawing implements and a fork. Each list item appears as a bitmap followed by the name of the object. A button prompts the user to select one item that is not like the others. Choosing the button with the fork selected displays a "You're right!" message and closes the dialog box. Choosing the button with any other list item selected displays a "Try again!" message.
The list box has the LBS_OWNERDRAW and LBS_HASSTRINGS styles, in addition to the standard list box styles. The code initializes the list box by sending the LB_ADDSTRING message to set the text, and then sends the LB_SETITEMDATA message to associate a bitmap with each list box item. The code also sets the height of each list box item by processing the WM_MEASUREITEM message and draws the text and bitmap for each item by processing the WM_DRAWITEM message.
#define XBITMAP 80
#define YBITMAP 20
#define BUFFER MAX_PATH
HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork;
HBITMAP hbmpPicture, hbmpOld;
void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp)
{
int nItem;
nItem = SendMessage(hwndList, LB_ADDSTRING, 0, lpstr);
SendMessage(hwndList, LB_SETITEMDATA, nItem, hbmp);
}
DWORD APIENTRY DlgDrawProc(
HWND hDlg, /* window handle of dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{
int nItem;
TCHAR tchBuffer[BUFFER];
HBITMAP hbmp;
HWND hListBox;
TEXTMETRIC tm;
int y;
HDC hdcMem;
LPMEASUREITEMSTRUCT lpmis;
LPDRAWITEMSTRUCT lpdis;
RECT rcBitmap;
switch (message) {
case WM_INITDIALOG:
/* Load bitmaps. */
hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700));
hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701));
hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702));
hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703));
hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704));
/* Retrieve list box handle. */
hListBox = GetDlgItem(hDlg, IDL_STUFF);
/*
* Initialize the list box text and associate a bitmap
* with each list box item.
*/
AddItem(hListBox, "pencil", hbmpPencil);
AddItem(hListBox, "crayon", hbmpCrayon);
AddItem(hListBox, "marker", hbmpMarker);
AddItem(hListBox, "pen", hbmpPen);
AddItem(hListBox, "fork", hbmpFork);
SetFocus(hListBox);
SendMessage(hListBox, LB_SETCURSEL, 0, 0);
return TRUE;
case WM_MEASUREITEM:
lpmis = (LPMEASUREITEMSTRUCT) lParam;
/* Set the height of the list box items. */
lpmis->itemHeight = 20;
return TRUE;
case WM_DRAWITEM:
lpdis = (LPDRAWITEMSTRUCT) lParam;
/* If there are no list box items, skip this message. */
if (lpdis->itemID == -1) {
break;
}
/*
* Draw the bitmap and text for the list box item. Draw a
* rectangle around the bitmap if it is selected.
*/
switch (lpdis->itemAction) {
case ODA_SELECT:
case ODA_DRAWENTIRE:
/* Display the bitmap associated with the item. */
hbmpPicture = (HBITMAP) SendMessage(lpdis->hwndItem,
LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0);
hdcMem = CreateCompatibleDC(lpdis->hDC);
hbmpOld = SelectObject(hdcMem, hbmpPicture);
BitBlt(lpdis->hDC,
lpdis->rcItem.left, lpdis->rcItem.top,
lpdis->rcItem.right - lpdis->rcItem.left,
lpdis->rcItem.bottom - lpdis->rcItem.top,
hdcMem, 0, 0, SRCCOPY);
/* Display the text associated with the item. */
SendMessage(lpdis->hwndItem, LB_GETTEXT,
lpdis->itemID, (LPARAM) tchBuffer);
GetTextMetrics(lpdis->hDC, &tm);
y = (lpdis->rcItem.bottom + lpdis->rcItem.top -
tm.tmHeight) / 2;
TextOut(lpdis->hDC,
XBITMAP + 6,
y,
tchBuffer,
strlen(tchBuffer));
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
/* Is the item selected? */
if (lpdis->itemState & ODS_SELECTED) {
/*
* Set RECT coordinates to surround only the
* bitmap.
*/
rcBitmap.left = lpdis->rcItem.left;
rcBitmap.top = lpdis->rcItem.top;
rcBitmap.right = lpdis->rcItem.left + XBITMAP;
rcBitmap.bottom = lpdis->rcItem.top + YBITMAP;
/*
* Draw a rectangle around bitmap to indicate
* the selection.
*/
DrawFocusRect(lpdis->hDC, &rcBitmap);
}
break;
case ODA_FOCUS:
/*
* Do not process focus changes. The focus caret
* (outline rectangle) indicates the selection.
* The Which one? (IDOK) button indicates the final
* selection.
*/
break;
}
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
/* Get the selected item's text. */
nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF),
LB_GETCURSEL, 0, (LPARAM) 0);
hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF),
LB_GETITEMDATA, nItem, 0);
/*
* If the item is not the correct answer, tell the
* user to try again.
*
* If the item is the correct answer, congratulate
* the user and destroy the dialog box.
*/
if (hbmp != hbmpFork) {
MessageBox(hDlg, "Try again!", "Oops.", MB_OK);
return FALSE;
}
else {
MessageBox(hDlg, "You're right!",
"Congratulations.", MB_OK);
/* Fall through. */
}
case IDCANCEL:
/* Destroy the dialog box. */
EndDialog(hDlg, TRUE);
return TRUE;
default:
return FALSE;
}
case WM_DESTROY:
/* Free any resources used by the bitmaps. */
DeleteObject(hbmpPencil);
DeleteObject(hbmpCrayon);
DeleteObject(hbmpMarker);
DeleteObject(hbmpPen);
DeleteObject(hbmpFork);
return TRUE;
default:
return FALSE;
}
return FALSE;
}