Строка меню и раскрывающееся меню определены вначале расширенным ресурсом шаблона меню. Поскольку шаблон меню не может определять "собственные" пункты, меню вначале содержит четыре текстовых пункта меню со следующими строками: "Regular" ("Обычный"), "Bold" ("Полужирный"), "Italic" ("Курсив") и "UnderlineWM_CREATE. Когда процедура получает сообщение WM_CREATE, она вызывает определяемую программой функцию OnCreate, которая выполняет следующие шаги для каждого пункта меню: - Распределяет в памяти определяемую программой структуру MYITEM.
- Получает текст пункта меню и сохраняет его в определяемой программой структуре MYITEM.
- Создает шрифт, использованный, чтобы показать на экране пункт меню и сохраняет дескриптор в определяемой программой структуре MYITEM.
- Изменяет тип пункта меню на MFT_OWNERDRAW и сохраняет указатель на определяемую программой структуру MYITEM, как на данные пункта.
Поскольку указатель на каждую определяемую программой структуру MYITEM сохраняется как данные пункта, он переходит в оконную процедуру вместе с сообщениями WM_MEASUREITEM и WM_DRAWITEM для соответствующего пункта меню. Указатель содержится в члене itemData и структуры MEASUREITEMSTRUCT и структуры DRAWITEMSTRUCT. Сообщение WM_MEASUREITEM отправляется для каждого "собственного" пункта меню, когда он впервые отображается на экране. Приложение обрабатывает это сообщение, выбирая шрифт для пункта меню в контекст устройства, а затем устанавливает пространство, требуемое, чтобы показать на экране текст пункта меню этим шрифтом. Шрифт и текст пункта меню оба определены структурой пункта меню MYITEM (структура, определенная прикладной программой). Приложение устанавливает размер текста при помощи использования функции GetTextExtentPoint32. Оконная процедура обрабатывает сообщение WM_DRAWITEM, показывая на экране текст пункта меню в соответствующем шрифте. Шрифт и текст пункта меню оба определены структурой пункта меню MYITEM. Прикладная программа выбирает текст и цвета фона, соответствующие состоянию пункта меню. Оконная процедура обрабатывает сообщение WM_DESTROY, чтобы уничтожить шрифты и освободить память. Прикладная программа удаляет шрифт и освобождает определяемую программой структуру MYITEM для каждого пункта меню. Ниже следуют необходимые части заголовочного файла прикладной программы. // Идентификаторы пункта меню для меню Character (Шрифт)
#define IDM_CHARACTER 10 #define IDM_REGULAR 11 #define IDM_BOLD 12 #define IDM_ITALIC 13 #define IDM_UNDERLINE 14
// Структура, связанная с пунктами меню
typedef struct tagMYITEM { HFONT hfont; int cchItemText; char szItemText[1]; } MYITEM;
#define CCH_MAXITEMTEXT 256
Ниже следуют необходимые части оконной процедуры прикладной программы и связанных функций. LRESULT CALLBACK MainWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch (uMsg) { case WM_CREATE: if (!OnCreate(hwnd)) return -1; break;
case WM_DESTROY: OnDestroy(hwnd); PostQuitMessage(0); break;
case WM_MEASUREITEM: OnMeasureItem(hwnd, (LPMEASUREITEMSTRUCT) lParam);
return TRUE;
case WM_DRAWITEM: OnDrawItem(hwnd, (LPDRAWITEMSTRUCT) lParam); return TRUE;
. . // Дополнительная обработка сообщения идет здесь. .
default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; }
BOOL WINAPI OnCreate(HWND hwnd) { HMENU hmenuBar = GetMenu(hwnd); HMENU hmenuPopup; MENUITEMINFO mii; UINT uID;
MYITEM *pMyItem;
// Получим дескриптор выскакивающего меню.
mii.fMask = MIIM_SUBMENU; <>// чтобы получить информацию GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); hmenuPopup = mii.hSubMenu;
// Модифицируем каждый пункт меню. Предположим, что // IDs ( идентификаторы) от IDM_REGULAR до // IDM_UNDERLINE - последовательные числа
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {
// Распределим в памяти структуру пункта, // оставляя пространство для строки плюс для // символов CCH_MAXITEMTEXT.
pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED, sizeof(MYITEM) + CCH_MAXITEMTEXT);
// Сохраним текст пункта в структуре пункта
mii.fMask = MIIM_TYPE; mii.dwTypeData = pMyItem->szItemText; mii.cch = CCH_MAXITEMTEXT; GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); pMyItem->cchItemText = mii.cch;
// Перераспределим память для структуры для // минимально требуемого размера.
pMyItem = (MYITEM *) LocalReAlloc(pMyItem, sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);
// Создадим шрифт, который используем для прорисовки пункта.
pMyItem->hfont = CreateMenuItemFont(uID);
// Заменим пункт на "собственный" пункт и сохраним // адрес структуры пункта как данные пункта.
mii.fMask = MIIM_TYPE | MIIM_DATA; mii.fType = MFT_OWNERDRAW; mii.dwItemData = (DWORD) pMyItem;
SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); } return TRUE; }
HFONT CreateMenuItemFont(UINT uID) { LOGFONT lf;
ZeroMemory(&lf, sizeof(lf)); lf.lfHeight = 20; lstrcpy(lf.lfFaceName, "Times New Roman");
switch (uID) { case IDM_BOLD: lf.lfWeight = FW_HEAVY; break;
case IDM_ITALIC: lf.lfItalic = TRUE; break;
case IDM_UNDERLINE:
lf.lfUnderline = TRUE; break; } return CreateFontIndirect(&lf); }
VOID WINAPI OnDestroy(HWND hwnd) { HMENU hmenuBar = GetMenu(hwnd); HMENU hmenuPopup; MENUITEMINFO mii; UINT uID; MYITEM *pMyItem;
// Получим дескриптор меню.
mii.fMask = MIIM_SUBMENU; // чтобы получить информацию GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); hmenuPopup = mii.hSubMenu;
// Освободим ресурсы, связанные с каждым пунктом меню.
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {
// Получим данные пункта.
mii.fMask = MIIM_DATA; GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); pMyItem = (MYITEM *) mii.dwItemData;
// Уничтожим шрифт и освободим структуру пункта
DeleteObject(pMyItem->hfont); LocalFree(pMyItem); } }
VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis) { MYITEM *pMyItem = (MYITEM *) lpmis->itemData; HDC hdc = GetDC(hwnd); HFONT hfntOld = SelectObject(hdc, pMyItem->hfont); SIZE size; GetTextExtentPoint32(hdc, pMyItem->szItemText, pMyItem->cchItemText, &size); lpmis->itemWidth = size.cx; lpmis->itemHeight = size.cy; SelectObject(hdc, hfntOld); ReleaseDC(hwnd, hdc); }
VOID WINAPI OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis) { MYITEM *pMyItem = (MYITEM *) lpdis->itemData; COLORREF clrPrevText, clrPrevBkgnd; HFONT hfntPrev; int x, y; // Установим соответствующие цвета шрифта и фона. if (lpdis->itemState & ODS_SELECTED) { clrPrevText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); clrPrevBkgnd = SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT)); } else { clrPrevText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_MENUTEXT)); clrPrevBkgnd = SetBkColor(lpdis->hDC,
GetSysColor(COLOR_MENU)); }
// Решим, где нарисовать и оставить пространство для "галочки". x = lpdis->rcItem.left; y = lpdis->rcItem.top; x += LOWORD(GetMenuCheckMarkDimensions());
// Выберем шрифт и прорисуем текст.
hfntPrev = SelectObject(lpdis->hDC, pMyItem->hfont); ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE, &lpdis->rcItem, pMyItem->szItemText, pMyItem->cchItemText, NULL);
// Восстанавливаем первоначальный шрифт и цвет.
SelectObject(lpdis->hDC, hfntPrev); SetTextColor(lpdis->hDC, clrPrevText); SetBkColor(lpdis->hDC, clrPrevBkgnd); }
|