Пример пунктов собственного меню

Пример в этой теме использует в меню пункты собственного меню. Пункты меню выбирают конкретные атрибуты шрифта, а прикладная программа показывает на экране каждый пункт меню, используя шрифт, который имеет соответствующий атрибут. Например, пункт меню 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); 
} 

 

Назад в оглавление
На главную страницу

Hosted by uCoz