Создание поля со списком нарисованного владельцем
В этом разделе показано, как использовать поле со списком нарисованного владельцем. В примере кода C++ для отображения четырех групп продуктов питания, представленных растровым изображением и именем, используется раскрывающийся список владельца. Выбор группы продуктов питания приводит к отображению продуктов в этой группе в списке.
Диалоговое окно также содержит поле списка (IDLIST) и две кнопки: ОК (IDOK) и Отмена (IDCANCEL). Константы IDOK и IDCANCEL определяются файлами заголовков ПАКЕТА SDK. Константный IDLIST определяется в файле заголовка приложения, как идентификатор элемента управления, IDCO МБ O. Дополнительные сведения о диалоговых окнах см . в диалоговом окне.
Это важно знать
Технологии
Необходимые компоненты
- C/C++
- Программирование пользовательского интерфейса Windows
Instructions
Шаг 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
приложением функцию, чтобы инициализировать поле списка.
В этом примере поле со списком нарисованного владельца является раскрывающимся списком, которое содержит имена каждой из четырех групп продуктов питания. 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 для связывания дескриптора растрового изображения с каждым элементом списка. Процедура окна извлекает дескриптор растрового изображения из элемента itemData структуры DRAWITEMSTRUCT. Система использует два растровых рисунка для каждого символа группы продуктов питания: монохромное растровое изображение с операцией растра 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 . В примере обрабатываются уведомления из поля со списком, поля списка и кнопки "ОК ". Идентификатор элемента управления находится в слове с низким порядком wParam, и код уведомления находится в слове wParam с высоким порядком.
Если идентификатор элемента управления — IDCO МБ O, событие произошло в поле со списком. В ответ процедура диалогового окна игнорирует все остальные события со списком со списком, кроме CBN_SELENDOK, что указывает на то, что был сделан выбор, раскрывающийся список был закрыт, и изменения должны приниматься. Процедура диалогового окна вызывает InitFoodList
сброс содержимого поля списка и добавление имен текущих выбранных элементов в раскрывающемся списке.
Если идентификатор элемента управления имеет идентификатор IDLIST, событие произошло в поле списка. Это приводит к тому, что процедура диалогового окна пропускает все события списка, кроме LBN_DBLCLK, что означает, что пользователь дважды щелкнул элемент списка. Это событие обрабатывается таким же образом, как если бы была выбрана кнопка "ОК ".
Если идентификатор элемента управления имеет идентификатор IDOK, пользователь выбрал кнопку "ОК ". В ответ процедура диалогового окна вставляет имя выбранной пищи в многострочный элемент управления редактирования приложения, а затем вызывает функцию 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;
Полный пример
Ниже приведена процедура диалогового окна и вспомогательные функции для диалогового окна "Квадратная еда ".
#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));
}
См. также