Пример в этой теме использует в меню пункты собственного меню. Пункты меню выбирают конкретные атрибуты шрифта, а прикладная программа показывает на экране каждый пункт меню, используя шрифт, который имеет соответствующий атрибут. Например, пункт меню Italic (Курсив) отображается в курсивном шрифте. Меню с именем Character (Шрифт) в строке меню открывает меню.
Строка меню и раскрывающееся меню определяются вначале ресурсом улучшенного шаблона меню. Поскольку шаблон меню не может определять собственные пункты, меню вначале содержит четыре текстовых пункта меню со следующими строками: "Regular" ("Обычный"), "Bold " ("Полужирный"), "Italic" ("Курсив") и "Underline" ("Подчеркнутый"). Оконная процедура прикладной программы заменяет их собственными пунктами, когда она обрабатывает сообщение WM_CREATE. Когда процедура получает сообщение WM_CREATE, она вызывает определяемую программой функцию OnCreate, которая выполняет следующие шаги для каждого пункта меню:
Поскольку указатель на каждую определяемую программой структуру 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;
// Модифицируем каждый пункт меню. Присвоим идентификаторам от
// 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_STRING;
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_FTYPE | 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 += GetSystemMetrics(SM_CXMENUCHECK);
// Выберем шрифт и нарисуем текст.
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);
}
|