Эта тема содержит пример из прикладной программы, которая использует нарисованные пользователем пункты в меню. Меню содержит пункты, которые устанавливают атрибуты текущего шрифта, а пункты отображаются, используя соответствующий атрибут шрифта.
Здесь меню, заданное в файле определения ресурса. Обратите внимание, что строки для пунктов меню Regular (Обычный), Bold (Полужирный), Italic (Курсив) и Underline (Подчеркнутый) назначены во время выполнения программы, так что их строки пусты в файле определения ресурса.
MainMenu MENU BEGIN POPUP "&Character" BEGIN MENUITEM "", IDM_REGULAR MENUITEM SEPARATOR MENUITEM "", IDM_BOLD MENUITEM "", IDM_ITALIC MENUITEM "", IDM_ULINE END END
Оконная процедура прикладной программы обрабатывает сообщения, включающие использование нарисованных пользователем пунктов меню. Приложение использует сообщение WM_CREATE, чтобы сделать следующее:
Установить флажок MF_OWNERDRAW для пунктов меню.
Установить строки текста для пунктов меню.
Получить дескрипторы шрифтов, использованных для прорисовки этих пунктов.
Получить значения цвета текста и фона для выбранных пунктов меню.
Дескрипторы текстовых строк и шрифта сохраняются в массиве определяемых программой структур MYITEM. Определяемая программой функция GetAFont создает шрифт, который соответствует атрибутам данного шрифта и возвращает значение дескриптора шрифта. Дескрипторы разрушаются в ходе обработки сообщения WM_DESTROY.
В ходе обработки сообщения WM_MEASUREITEM, пример получает ширину и высоту строки пункта меню и копирует эти значения в структуру MEASUREITEMSTRUCT. Windows использует значения ширины и высоты, чтобы вычислить размер меню.
В ходе обработки сообщения WM_DRAWITEM, строка пункта меню рисуется, оставляя место рядом со строкой для значка "галочки ". Если пользователь выбирает пункт, цвет выбранного текста и фона используются, чтобы прорисовать пункт.
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam) HWND hwnd; UINT uMsg; WPARAM wParam; LPARAM lParam; { typedef struct _MYITEM { HFONT hfont; LPSTR psz; } MYITEM; // структура для шрифта и строки пункта MYITEM *pmyitem; // указатель на шрифт и строку пункта static MYITEM myitem[CITEMS]; // массив MYITEMS static HMENU hmenu; // дескриптор главного меню static COLORREF crSelText; // цвет текста выбранного пункта static COLORREF crSelBkgnd; // цвет фона выбранного пункта COLORREF crText; // цвет текста невыбранного пункта COLORREF crBkgnd; // цвет фона невыбранного пункта LPMEASUREITEMSTRUCT lpmis; // указывает на данные пункта LPDRAWITEMSTRUCT lpdis; // указывает на данные для прорисовки пункта HDC hdc; // дескриптор экранного DC SIZE size; // протяженность текста пункта меню DWORD dwCheckXY; // размеры метки «галочка» WORD wCheckX; // ширина метки «галочка» int nTextX; // ширина пункта меню int nTextY; // высота пункта меню int i; // цикл счета HFONT hfontOld; // дескриптор старого шрифта BOOL fSelected = FALSE; // флажок выбора пункта меню switch (uMsg) { case WM_CREATE: // Изменим пункты меню Regular (Обычный), Bold (Полужирный), Italic // (Курсив) и Underline (Подчеркнутый), чтобы делать их нарисованными // пользователем пунктами. Свяжем структуру MYITEM с каждым пунктом, // которые содержат строку и шрифт, чтобы обработать каждый пункт. hmenu = GetMenu(hwnd); ModifyMenu(hmenu, IDM_REGULAR, MF_BYCOMMAND | MF_CHECKED | MF_OWNERDRAW, IDM_REGULAR, (LPTSTR) &myitem[REGULAR]); ModifyMenu(hmenu, IDM_BOLD, MF_BYCOMMAND | MF_OWNERDRAW, IDM_BOLD, (LPTSTR) &myitem[BOLD]); ModifyMenu(hmenu, IDM_ITALIC, MF_BYCOMMAND | MF_OWNERDRAW, IDM_ITALIC, (LPTSTR) &myitem[ITALIC]); ModifyMenu(hmenu, IDM_ULINE, MF_BYCOMMAND | MF_OWNERDRAW, IDM_ULINE,(LPTSTR)&myitem[ULINE]); // Извлечем дескриптор шрифта каждого пункта и скопируем их в // элемент hfont структуры MYITEM каждого пункта. // А также, скопируем в структуры строку каждого пункта myitem[REGULAR].hfont = GetAFont(REGULAR); myitem[REGULAR].psz = "Regular"; myitem[BOLD].hfont = GetAFont(BOLD); myitem[BOLD].psz = "Bold"; myitem[ITALIC].hfont = GetAFont(ITALIC); myitem[ITALIC].psz = "Italic"; myitem[ULINE].hfont = GetAFont(ULINE); myitem[ULINE].psz = "Underline"; // Извлечем текст и цвет фона выбранного текста меню. crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT); crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT); return 0; case WM_MEASUREITEM: // Извлечем контекст устройства главного окна. hdc = GetDC(hwnd); // Извлечем указатели на пункты меню в // структурах MEASUREITEMSTRUCT и MYITEM. lpmis = (LPMEASUREITEMSTRUCT) lParam; pmyitem = (MYITEM *) lpmis->itemData; // Выберем шрифт, связанный пунктом в // контексте устройства главного окна. hfontOld = SelectObject(hdc, pmyitem->hfont); // Извлечем ширину и высоту строки пункта, // а затем скопируем их в элементы itemWidth // и itemHeight структуры MEASUREITEMSTRUCT. GetTextExtentPoint32(hdc, pmyitem->psz, lstrlen(pmyitem->psz), &size); lpmis->itemWidth = size.cx; lpmis->itemHeight = size.cy; // В контексте устройства обратно выберем старый // шрифт, и затем освободим контекст устройства. SelectObject(hdc, hfontOld); ReleaseDC(hwnd, hdc); return TRUE; break; case WM_DRAWITEM: // Получим указатели на пункты меню в структурах // DRAWITEMSTRUCT и MYITEM. lpdis = (LPDRAWITEMSTRUCT) lParam; pmyitem = (MYITEM *) lpdis->itemData; // Если пользователь выбрал пункт, используйте выбранный // текст и цвет фона для отображения пункта. if (lpdis->itemState & ODS_SELECTED) { crText = SetTextColor(lpdis->hDC, crSelText); crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd); fSelected = TRUE; } // Не забудьте оставить пространство в пункте // меню для значка "галочки ". Извлеките ширину значка // и добавьте ее к ширине пункта меню. dwCheckXY = GetMenuCheckMarkDimensions(); wCheckX = LOWORD(dwCheckXY); nTextX = wCheckX + lpdis->rcItem.left; nTextY = lpdis->rcItem.top; // Выберите шрифт, связанный с пунктом в контексте // устройства пункта, а затем нарисуйте строку. hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont); ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE, &lpdis->rcItem, pmyitem->psz, lstrlen(pmyitem->psz), NULL); // Выберите обратно предыдущий шрифт в контексте устройства. SelectObject(lpdis->hDC, hfontOld); // Возвратите текст и цвета фона в // их нормальное состояние (не выбранное). if (fSelected) { SetTextColor(lpdis->hDC, crText); SetBkColor(lpdis->hDC, crBkgnd); } return TRUE; . . // Обработка других сообщений. . case WM_DESTROY: // Уничтожим дескрипторы шрифта пунктов меню. for (i = 0; i < CITEMS; i++) DeleteObject(myitem[i].hfont); PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return NULL; } HFONT GetAFont(fnFont) int fnFont; // флажки атрибутов шрифта { static LOGFONT lf; // структура для информации о шрифте // Получим дескриптор для моноширинного шрифта ANSI, а информацию о шрифте скопируем // в структуру LOGFONT. GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT), &lf); // Установим соответствующие атрибуты шрифта. if (fnFont == BOLD) lf.lfWeight = FW_BOLD; else lf.lfWeight = FW_NORMAL; lf.lfItalic = (fnFont == ITALIC); lf.lfItalic = (fnFont == ULINE); // создадим шрифт, а затем возвратим его дескриптор. return CreateFont(lf.lfHeight, lf.lfWidth, lf.lfEscapement, lf.lfOrientation, lf.lfWeight, lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet, lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality, lf.lfPitchAndFamily, lf.lfFaceName); }