Поделиться через


Использование меню

В этом разделе описываются следующие задачи:

Использование ресурса Menu-Template

Обычно вы включаете меню в приложение, создавая ресурс шаблона меню, а затем загружаете меню во время выполнения. В этом разделе описывается формат шаблона меню и объясняется, как загрузить ресурс шаблона меню и использовать его в приложении. Сведения о создании ресурса шаблона меню см. в документации, входящей в комплект средств разработки.

Расширенный формат Menu-Template

Формат расширенного меню поддерживает дополнительные функции меню. Как и стандартные ресурсы шаблонов меню, ресурсы расширенного меню имеют тип ресурса RT_MENU. Система различает два формата ресурсов по номеру версии, который является первым членом заголовка ресурса.

Шаблон расширенного меню состоит из структуры MENUEX_TEMPLATE_HEADER, за которой следуют еще две структуры определения элементов MENUEX_TEMPLATE_ITEM.

Старый формат Menu-Template

Старый шаблон меню (Microsoft Windows NT 3.51 и более ранних версий) определяет меню, но не поддерживает новые функции меню. Старый ресурс шаблона меню имеет тип ресурса RT_MENU.

Старый шаблон меню состоит из структуры MENUITEMTEMPLATEHEADER, за которой следует одна или несколько структур MENUITEMTEMPLATE.

Загрузка ресурса Menu-Template

Чтобы загрузить ресурс шаблона меню, используйте функцию LoadMenu, указав дескриптор модуля, который содержит ресурс и идентификатор шаблона меню. Функция LoadMenu возвращает дескриптор меню, который можно использовать для назначения меню окну. Это окно становится окном владельца меню, получая все сообщения, созданные меню.

Чтобы создать меню из шаблона меню, который уже находится в памяти, используйте функцию LoadMenuIndirect. Это полезно, если приложение динамически создает шаблоны меню.

Чтобы назначить меню окну, используйте функцию SetMenuили укажите дескриптор меню в параметре hMenu функции CreateWindowEx при создании окна. Другой способ, который можно назначить меню окну, — указать шаблон меню при регистрации класса окна; Шаблон определяет указанное меню в качестве меню класса для этого класса.

Чтобы система автоматически назначить определенное меню окну, укажите шаблон меню при регистрации класса окна. Шаблон определяет указанное меню как меню класса для этого оконного класса. Затем при создании окна указанного класса система автоматически назначает указанное меню окну.

Невозможно назначить меню в окне, которое является дочерним окном.

Чтобы создать меню класса, добавьте идентификатор ресурса шаблона меню в элемент lpszMenuName структуры WNDCLASS. Затем передайте указатель этой структуры функции RegisterClass.

Создание меню класса

В следующем примере показано, как создать меню класса для приложения, создать окно, использующее меню класса, и обрабатывать команды меню в процедуре окна.

Ниже приведена соответствующая часть файла заголовка приложения:

// Menu-template resource identifier 
 
#define IDM_MYMENURESOURCE   3

Ниже приведены соответствующие части самого приложения:

HINSTANCE hinst; 
 
int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{ 
    MSG msg = { };  // message 
    WNDCLASS wc;    // windowclass data 
    HWND hwnd;      // handle to the main window 
 
    // Create the window class for the main window. Specify 
    // the identifier of the menu-template resource as the 
    // lpszMenuName member of the WNDCLASS structure to create 
    // the class menu. 
 
    wc.style = 0; 
    wc.lpfnWndProc = (WNDPROC) MainWndProc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hinstance; 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
    wc.lpszMenuName =  MAKEINTRESOURCE(IDM_MYMENURESOURCE); 
    wc.lpszClassName = "MainWClass"; 
 
    if (!RegisterClass(&wc)) 
        return FALSE; 
 
    hinst = hinstance; 
 
    // Create the main window. Set the hmenu parameter to NULL so 
    // that the system uses the class menu for the window. 
 
    hwnd = CreateWindow("MainWClass", "Sample Application", 
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 
        CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance, 
        NULL); 
 
    if (hwnd == NULL) 
        return FALSE; 
 
    // Make the window visible and send a WM_PAINT message to the 
    // window procedure. 
 
    ShowWindow(hwnd, nCmdShow); 
    UpdateWindow(hwnd); 
 
    // Start the main message loop. 
 
    while (GetMessage(&msg, NULL, 0, 0)) 
    { 
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    } 
    return msg.wParam; 
        UNREFERENCED_PARAMETER(hPrevInstance); 
} 
 
 
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
 
    switch (uMsg) 
    { 
        // Process other window messages. 
 
        case WM_COMMAND: 
 
            // Test for the identifier of a command item. 
 
            switch(LOWORD(wParam)) 
            { 
                case IDM_FI_OPEN: 
                    DoFileOpen();   // application-defined 
                    break; 
 
                case IDM_FI_CLOSE: 
                    DoFileClose();  // application-defined 
                    break; 
                // Process other menu commands. 
 
                default: 
                    break; 
 
            } 
            return 0; 
 
        // Process other window messages. 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return NULL; 
} 

Создание контекстного меню

Чтобы использовать контекстное меню в приложении, передайте его дескриптор в функцию TrackPopupMenuEx. Приложение обычно вызывает TrackPopupMenuEx в процедуре окна в ответ на созданное пользователем сообщение, например WM_LBUTTONDOWN или WM_KEYDOWN.

Помимо обработчика всплывающего меню, TrackPopupMenuEx требует указать дескриптор окна владельца, положение контекстного меню (в координатах экрана) и кнопку мыши, которую пользователь может использовать для выбора элемента.

Старая функция TrackPopupMenu по-прежнему поддерживается, но новые приложения должны использовать функцию TrackPopupMenuEx. Функция TrackPopupMenuEx требует одинаковых параметров, что и TrackPopupMenu, но также позволяет указать часть экрана, которую меню не должно скрывать. Приложение обычно вызывает эти функции в процедуре окна при обработке сообщения WM_CONTEXTMENU.

Вы можете указать положение контекстного меню, указав координаты x и y вместе с флагом TPM_CENTERALIGN, TPM_LEFTALIGNили флагом TPM_RIGHTALIGN. Флаг указывает положение контекстного меню относительно координат x и y.

Вы должны разрешить пользователю выбрать элемент из контекстного меню с помощью той же кнопки мыши, которая использовалась для отображения меню. Для этого укажите флаг TPM_LEFTBUTTON или TPM_RIGHTBUTTON, чтобы указать, какую кнопку мыши пользователь может использовать для выбора элемента меню.

Обработка сообщения WM_CONTEXTMENU

Сообщение WM_CONTEXTMENU создается, когда процедура окна приложения передает сообщение WM_RBUTTONUP или WM_NCRBUTTONUP функции DefWindowProc. Приложение может обработать это сообщение, чтобы отобразить контекстное меню, соответствующее определенной части экрана. Если приложение не отображает контекстное меню, оно должно передать сообщение в DefWindowProc для обработки по умолчанию.

Ниже приведен пример обработки сообщений WM_CONTEXTMENU, как это может отображаться в процедуре окна приложения. Младшие и старшие слова параметра lParam определяют координаты экрана при отпускании правой кнопки мыши (обратите внимание, что эти координаты могут принимать отрицательные значения в системах с несколькими мониторами). Функция, определяемая приложением OnContextMenu, возвращает TRUE, если она отображает контекстное меню или FALSE, если она не отображается.

case WM_CONTEXTMENU: 
    if (!OnContextMenu(hwnd, GET_X_LPARAM(lParam),
              GET_Y_LPARAM(lParam))) 
        return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    break; 

Следующая функция OnContextMenu отображает контекстное меню, если указанная позиция мыши находится в клиентской области окна. Более сложная функция может отображать одно из нескольких различных меню в зависимости от того, какая часть клиентской области указана. Для отображения контекстного меню в этом примере вызывается определяемая приложением функция DisplayContextMenu. Описание этой функции см. в разделе Отображение контекстного меню.

BOOL WINAPI OnContextMenu(HWND hwnd, int x, int y) 
{ 
    RECT rc;                    // client area of window 
    POINT pt = { x, y };        // location of mouse click 
 
    // Get the bounding rectangle of the client area. 
 
    GetClientRect(hwnd, &rc); 
 
    // Convert the mouse position to client coordinates. 
 
    ScreenToClient(hwnd, &pt); 
 
    // If the position is in the client area, display a  
    // shortcut menu. 
 
    if (PtInRect(&rc, pt)) 
    { 
        ClientToScreen(hwnd, &pt); 
        DisplayContextMenu(hwnd, pt); 
        return TRUE; 
    } 
 
    // Return FALSE if no menu is displayed. 
 
    return FALSE; 
} 

Создание контекстного меню Font-Attributes

В примере этого раздела содержатся части кода из приложения, создающего и отображающего контекстное меню, позволяющее пользователю задавать шрифты и атрибуты шрифтов. Приложение отображает меню в клиентской области его главного окна всякий раз, когда пользователь нажимает левую кнопку мыши.

Ниже приведен шаблон меню контекстного меню, предоставленного в файле определения ресурса приложения.

PopupMenu MENU 
BEGIN 
  POPUP "Dummy Popup" 
    BEGIN 
      POPUP "Fonts" 
        BEGIN 
          MENUITEM "Courier",     IDM_FONT_COURIER 
          MENUITEM "Times Roman", IDM_FONT_TMSRMN 
          MENUITEM "Swiss",       IDM_FONT_SWISS 
          MENUITEM "Helvetica",   IDM_FONT_HELV 
          MENUITEM "Old English", IDM_FONT_OLDENG 
        END 
      POPUP "Sizes" 
        BEGIN 
          MENUITEM "7",  IDM_SIZE_7 
          MENUITEM "8",  IDM_SIZE_8 
          MENUITEM "9",  IDM_SIZE_9 
          MENUITEM "10", IDM_SIZE_10 
          MENUITEM "11", IDM_SIZE_11 
          MENUITEM "12", IDM_SIZE_12 
          MENUITEM "14", IDM_SIZE_14 
        END 
      POPUP "Styles" 
        BEGIN 
          MENUITEM "Bold",        IDM_STYLE_BOLD 
          MENUITEM "Italic",      IDM_STYLE_ITALIC 
          MENUITEM "Strike Out",  IDM_STYLE_SO 
          MENUITEM "Superscript", IDM_STYLE_SUPER 
          MENUITEM "Subscript",   IDM_STYLE_SUB 
        END 
    END 
 
END 

В следующем примере приведена процедура окна и вспомогательные функции, используемые для создания и отображения контекстного меню.

LRESULT APIENTRY MenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    RECT rc;    // client area             
    POINT pt;   // location of mouse click  
 
    switch (uMsg) 
    { 
        case WM_LBUTTONDOWN: 
 
            // Get the bounding rectangle of the client area. 
 
            GetClientRect(hwnd, (LPRECT) &rc); 
 
            // Get the client coordinates for the mouse click.  
 
            pt.x = GET_X_LPARAM(lParam); 
            pt.y = GET_Y_LPARAM(lParam); 
 
            // If the mouse click took place inside the client 
            // area, execute the application-defined function 
            // that displays the shortcut menu. 
 
            if (PtInRect((LPRECT) &rc, pt)) 
                HandlePopupMenu(hwnd, pt); 
            break; 
        // Process other window messages.  
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return NULL; 
} 
 
 
VOID APIENTRY HandlePopupMenu(HWND hwnd, POINT pt) 
{ 
    HMENU hmenu;            // menu template          
    HMENU hmenuTrackPopup;  // shortcut menu   
 
    //  Load the menu template containing the shortcut menu from the 
    //  application's resources. 
 
    hmenu = LoadMenu(hinst, "PopupMenu"); 
    if (hmenu == NULL) 
        return; 
 
    // Get the first shortcut menu in the menu template. This is the 
    // menu that TrackPopupMenu displays. 
 
    hmenuTrackPopup = GetSubMenu(hmenu, 0); 
 
    // TrackPopup uses screen coordinates, so convert the 
    // coordinates of the mouse click to screen coordinates. 
 
    ClientToScreen(hwnd, (LPPOINT) &pt); 
 
    // Draw and track the shortcut menu.  
 
    TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON, 
        pt.x, pt.y, 0, hwnd, NULL); 
 
    // Destroy the menu. 
 
    DestroyMenu(hmenu); 
} 

Отображение контекстного меню

Функция, показанная в следующем примере, отображает контекстное меню.

Приложение включает ресурс меню, идентифицированный строкой "ShortcutExample". Панель меню просто содержит название меню. Приложение использует функцию TrackPopupMenu для отображения меню, связанного с этим элементом меню. (Сама строка меню не отображается, так как TrackPopupMenu требует дескриптор на меню, подменю или для контекстного меню.)

VOID APIENTRY DisplayContextMenu(HWND hwnd, POINT pt) 
{ 
    HMENU hmenu;            // top-level menu 
    HMENU hmenuTrackPopup;  // shortcut menu 
 
    // Load the menu resource. 
 
    if ((hmenu = LoadMenu(hinst, "ShortcutExample")) == NULL) 
        return; 
 
    // TrackPopupMenu cannot display the menu bar so get 
    // a handle to the first shortcut menu. 
 
    hmenuTrackPopup = GetSubMenu(hmenu, 0); 
 
    // Display the shortcut menu. Track the right mouse 
    // button. 
 
    TrackPopupMenu(hmenuTrackPopup, 
            TPM_LEFTALIGN | TPM_RIGHTBUTTON, 
            pt.x, pt.y, 0, hwnd, NULL); 
 
    // Destroy the menu. 
 
    DestroyMenu(hmenu); 
} 

Использование растровых карт Menu-Item

Система может использовать растровое изображение вместо текстовой строки для отображения элемента меню. Чтобы использовать растровое изображение, необходимо задать флаг MIIM_BITMAP для элемента меню и указать дескриптор растрового изображения, который система должна отображать для элемента меню в hbmpItem члене структуры MENUITEMINFO. В этом разделе описывается использование растровых изображений для элементов меню.

Задание флага типа растрового изображения

Флаг MIIM_BITMAP или MF_BITMAP указывает системе использовать растровое изображение, а не текстовую строку для отображения элемента меню. Флаг MIIM_BITMAP или MF_BITMAP элемента меню должен быть установлен во время выполнения; Его нельзя задать в файле определения ресурсов.

Для новых приложений можно использовать функцию SetMenuItemInfo или InsertMenuItem, чтобы задать флаг типа MIIM_BITMAP. Чтобы изменить элемент меню из текстового элемента на элемент растрового изображения, используйте SetMenuItemInfo. Чтобы добавить новый элемент растрового изображения в меню, используйте функцию InsertMenuItem.

Приложения, написанные для более ранних версий системы, могут продолжать использовать функции ModifyMenu, InsertMenuили Функции AppendMenu для задания флага MF_BITMAP. Чтобы изменить элемент меню из текстового элемента строки на элемент растрового изображения, используйте ChangeMenu. Чтобы добавить новый элемент растрового изображения в меню, используйте флаг MF_BITMAP с функцией InsertMenu или AddMenu.

Создание растрового изображения

При установке флага типа MIIM_BITMAP или MF_BITMAP для элемента меню необходимо также указать дескриптор растрового изображения, отображаемого системой для элемента меню. Вы можете указать растровое изображение как ресурс растрового изображения или создать растровое изображение во время выполнения. При использовании ресурса растрового изображения можно использовать функцию loadBitmapдля загрузки растрового изображения и получения его дескриптора.

Чтобы создать растровое изображение во время выполнения, используйте функции интерфейса графического устройства Windows (GDI). GDI предоставляет несколько способов создания растрового изображения во время выполнения, но разработчики обычно используют следующий метод:

  1. Используйте функцию CreateCompatibleDC, чтобы создать контекст устройства, совместимый с контекстом устройства, используемым главным окном приложения.
  2. Используйте функцию CreateCompatibleBitmap, чтобы создать растровое изображение, совместимое с основным окном приложения, или функцию CreateBitmapдля создания монохромного растрового изображения.
  3. Используйте функцию SelectObject, чтобы выбрать растровое изображение в контексте совместимого устройства.
  4. Используйте функции рисования GDI, такие как Ellipse и LineTo, чтобы нарисовать изображение в растровом рисунке.

Для получения дополнительной информации см. растровые изображения.

Добавление строк и графов в меню

В следующем примере кода показано, как создать меню, содержащее растровые изображения элементов меню. Он создает два меню. Первое — это меню диаграмм, содержащее три пиктограммы пунктов меню: круговая диаграмма, линейный график и столбчатая диаграмма. В примере показано, как загрузить эти растровые изображения из файла ресурсов приложения, а затем использовать CreatePopupMenu и функции AppendMenu для создания элементов меню и меню.

Второе меню — это меню "Линии". Он содержит растровые изображения, показывающие стили линий, предоставляемые предопределенным пером в системе. Растровые изображения в линейном стиле создаются во время исполнения с помощью функций GDI.

Ниже приведены определения ресурсов растрового изображения в файле определения ресурсов приложения.

PIE BITMAP pie.bmp
LINE BITMAP line.bmp
BAR BITMAP bar.bmp

Ниже приведены соответствующие части файла заголовка приложения.

// Menu-item identifiers 
 
#define IDM_SOLID       PS_SOLID 
#define IDM_DASH        PS_DASH 
#define IDM_DASHDOT     PS_DASHDOT 
#define IDM_DASHDOTDOT  PS_DASHDOTDOT 
 
#define IDM_PIE  1 
#define IDM_LINE 2 
#define IDM_BAR  3 
 
// Line-type flags  
 
#define SOLID       0 
#define DOT         1 
#define DASH        2 
#define DASHDOT     3 
#define DASHDOTDOT  4 
 
// Count of pens  
 
#define CPENS 5 
 
// Chart-type flags  
 
#define PIE  1 
#define LINE 2 
#define BAR  3 
 
// Function prototypes  
 
LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM); 
VOID MakeChartMenu(HWND); 
VOID MakeLineMenu(HWND, HPEN, HBITMAP); 

В следующем примере показано, как меню и растровые изображения элементов меню создаются в приложении.

LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
 
    static HPEN hpen[CPENS]; 
    static HBITMAP hbmp[CPENS]; 
    int i; 
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
 
            // Create the Chart and Line menus.  
 
            MakeChartMenu(hwnd); 
            MakeLineMenu(hwnd, hpen, hbmp); 
            return 0; 
 
        // Process other window messages. 
 
        case WM_DESTROY: 
 
            for (i = 0; i < CPENS; i++) 
            { 
                DeleteObject(hbmp[i]); 
                DeleteObject(hpen[i]); 
            } 
 
            PostQuitMessage(0); 
            break; 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return NULL; 
} 
 
VOID MakeChartMenu(HWND hwnd) 
{ 
    HBITMAP hbmpPie;    // handle to pie chart bitmap   
    HBITMAP hbmpLine;   // handle to line chart bitmap  
    HBITMAP hbmpBar;    // handle to bar chart bitmap   
    HMENU hmenuMain;    // handle to main menu          
    HMENU hmenuChart;   // handle to Chart menu  
 
    // Load the pie, line, and bar chart bitmaps from the 
    // resource-definition file. 
 
    hbmpPie = LoadBitmap(hinst, MAKEINTRESOURCE(PIE)); 
    hbmpLine = LoadBitmap(hinst, MAKEINTRESOURCE(LINE)); 
    hbmpBar = LoadBitmap(hinst, MAKEINTRESOURCE(BAR)); 
 
    // Create the Chart menu and add it to the menu bar. 
    // Append the Pie, Line, and Bar menu items to the Chart 
    // menu. 
 
    hmenuMain = GetMenu(hwnd); 
    hmenuChart = CreatePopupMenu(); 
    AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (UINT) hmenuChart, 
        "Chart"); 
    AppendMenu(hmenuChart, MF_BITMAP, IDM_PIE, (LPCTSTR) hbmpPie); 
    AppendMenu(hmenuChart, MF_BITMAP, IDM_LINE, 
        (LPCTSTR) hbmpLine); 
    AppendMenu(hmenuChart, MF_BITMAP, IDM_BAR, (LPCTSTR) hbmpBar); 
 
    return; 
} 
 
VOID MakeLineMenu(HWND hwnd, HPEN phpen, HBITMAP phbmp) 
{ 
    HMENU hmenuLines;       // handle to Lines menu      
    HMENU hmenu;            // handle to main menu              
    COLORREF crMenuClr;     // menu-item background color       
    HBRUSH hbrBackground;   // handle to background brush       
    HBRUSH hbrOld;          // handle to previous brush         
    WORD wLineX;            // width of line bitmaps            
    WORD wLineY;            // height of line bitmaps           
    HDC hdcMain;            // handle to main window's DC       
    HDC hdcLines;           // handle to compatible DC          
    HBITMAP hbmpOld;        // handle to previous bitmap        
    int i;                  // loop counter                     
 
    // Create the Lines menu. Add it to the menu bar.  
 
    hmenu = GetMenu(hwnd); 
    hmenuLines = CreatePopupMenu(); 
    AppendMenu(hmenu, MF_STRING | MF_POPUP, 
        (UINT) hmenuLines, "&Lines"); 
 
    // Create a brush for the menu-item background color.  
 
    crMenuClr = GetSysColor(COLOR_MENU); 
    hbrBackground = CreateSolidBrush(crMenuClr); 
 
    // Create a compatible device context for the line bitmaps, 
    // and then select the background brush into it. 
 
    hdcMain = GetDC(hwnd); 
    hdcLines = CreateCompatibleDC(hdcMain); 
    hbrOld = SelectObject(hdcLines, hbrBackground); 
 
    // Get the dimensions of the check-mark bitmap. The width of 
    // the line bitmaps will be five times the width of the 
    // check-mark bitmap. 
 
    wLineX = GetSystemMetrics(SM_CXMENUCHECK) * (WORD) 5; 
    wLineY = GetSystemMetrics(SM_CYMENUCHECK); 
 
    // Create the bitmaps and select them, one at a time, into the 
    // compatible device context. Initialize each bitmap by 
    // filling it with the menu-item background color. 
 
    for (i = 0; i < CPENS; i++) 
    { 
        phbmp[i] = CreateCompatibleBitmap(hdcMain, wLineX, wLineY); 
        if (i == 0) 
            hbmpOld = SelectObject(hdcLines, phbmp[i]); 
        else 
            SelectObject(hdcLines, phbmp[i]); 
        ExtFloodFill(hdcLines, 0, 0, crMenuClr, FLOODFILLBORDER); 
    } 
 
    // Create the pens.  
 
    phpen[0] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); 
    phpen[1] = CreatePen(PS_DOT, 1, RGB(0, 0, 0)); 
    phpen[2] = CreatePen(PS_DASH, 1, RGB(0, 0, 0)); 
    phpen[3] = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0)); 
    phpen[4] = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0)); 
 
    // Select a pen and a bitmap into the compatible device 
    // context, draw a line into the bitmap, and then append 
    // the bitmap as an item in the Lines menu. 
 
    for (i = 0; i < CPENS; i++) 
    { 
        SelectObject(hdcLines, phbmp[i]); 
        SelectObject(hdcLines, phpen[i]); 
        MoveToEx(hdcLines, 0, wLineY / 2, NULL); 
        LineTo(hdcLines, wLineX, wLineY / 2); 
        AppendMenu(hmenuLines, MF_BITMAP, i + 1, 
            (LPCTSTR) phbmp[i]); 
    } 
 
    // Release the main window's device context and destroy the 
    // compatible device context. Also, destroy the background 
    // brush. 
 
    ReleaseDC(hwnd, hdcMain); 
    SelectObject(hdcLines, hbrOld); 
    DeleteObject(hbrBackground); 
    SelectObject(hdcLines, hbmpOld); 
    DeleteDC(hdcLines); 
 
    return; 
} 

Пример растровых карт Menu-Item

В примере в этом разделе создаются два меню, каждая из которых содержит несколько элементов меню растрового рисунка. Для каждого меню приложение добавляет соответствующее имя меню в строку меню главного окна.

Первое меню содержит пункты меню, показывающие каждый из трех типов диаграмм: круговая диаграмма, линия и линейчатая полоса. Растровые изображения для этих элементов меню определяются как ресурсы и загружаются с помощью функции LoadBitmap. Связанная с этим меню — это имя меню "Диаграмма" в строке меню.

Второе меню содержит пункты меню, показывающие каждый из пяти стилей строк, используемых с функцией CreatePen: PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOTи PS_DASHDOTDOT. Приложение создает растровые изображения для этих элементов меню во время выполнения с помощью функций рисования GDI. Связанное с этим меню — это имя строк меню в строке меню.

В процедуре окна приложения определены два статических массива дескрипторов растрового изображения. Один массив содержит дескрипторы трёх растровых изображений, используемых для меню диаграммы . Другой содержит дескрипторы пяти растровых изображений, используемых для меню строк . При обработке сообщения WM_CREATE процедура окна загружает растровые изображения диаграммы, создает растровые изображения линии, а затем добавляет соответствующие элементы меню. При обработке сообщения WM_DESTROY процедура окна удаляет все растровые изображения.

Ниже приведены соответствующие части файла заголовка приложения.

// Menu-item identifiers 
 
#define IDM_PIE         1 
#define IDM_LINE        2 
#define IDM_BAR         3 
 
#define IDM_SOLID       4 
#define IDM_DASH        5 
#define IDM_DASHDOT     6 
#define IDM_DASHDOTDOT  7 
 
// Number of items on the Chart and Lines menus 
 
#define C_LINES         5 
#define C_CHARTS        3 
 
// Bitmap resource identifiers 
 
#define IDB_PIE         1 
#define IDB_LINE        2 
#define IDB_BAR         3 
 
// Dimensions of the line bitmaps 
 
#define CX_LINEBMP      40 
#define CY_LINEBMP      10 

Ниже приведены соответствующие части процедуры обработки окна. Процедура окна выполняет большую часть своей инициализации путем вызова функций LoadChartBitmaps, CreateLineBitmaps и AddBitmapMenu, описанных далее в этом разделе.

LRESULT CALLBACK MainWindowProc( 
        HWND hwnd, 
        UINT uMsg, 
        WPARAM wParam, 
        LPARAM lParam 
        ) 
{ 
    static HBITMAP aHbmLines[C_LINES]; 
    static HBITMAP aHbmChart[C_CHARTS]; 
    int i; 
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
 
             // Call application-defined functions to load the 
             // bitmaps for the Chart menu and create those for 
             // the Lines menu. 
 
            LoadChartBitmaps(aHbmChart); 
            CreateLineBitmaps(aHbmLines); 
 
             // Call an application-defined function to create 
             // menus containing the bitmap menu items. The function 
             // also adds a menu name to the window's menu bar. 
 
            AddBitmapMenu( 
                    hwnd,      // menu bar's owner window 
                    "&Chart",  // text of menu name on menu bar 
                    IDM_PIE,   // ID of first item on menu 
                    aHbmChart, // array of bitmap handles 
                    C_CHARTS   // number of items on menu 
                    ); 
            AddBitmapMenu(hwnd, "&Lines", IDM_SOLID, 
                    aHbmLines, C_LINES); 
            break; 
 
        case WM_DESTROY: 
            for (i = 0; i < C_LINES; i++) 
                DeleteObject(aHbmLines[i]); 
            for (i = 0; i < C_CHARTS; i++) 
                DeleteObject(aHbmChart[i]); 
            PostQuitMessage(0); 
            break; 
 
        // Process additional messages here. 
 
        default: 
            return (DefWindowProc(hwnd, uMsg, wParam, lParam)); 
    } 
    return 0; 
} 

Функция LoadChartBitmaps, определяемая приложением, загружает растровые ресурсы для меню диаграммы, вызвав функцию LoadBitmap, как это показано ниже.

VOID WINAPI LoadChartBitmaps(HBITMAP *paHbm) 
{ 
    paHbm[0] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_PIE)); 
    paHbm[1] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_LINE)); 
    paHbm[2] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_BAR)); 
} 

Определяемая приложением функция CreateLineBitmaps создает растровые изображения для меню "Линии" с помощью функций рисования GDI. Функция создает контекст устройства памяти (DC) с теми же свойствами, что и контекст устройства окна рабочего стола. Для каждого стиля строки функция создает растровое изображение, выбирает его в контроллер памяти и рисует в нем.

VOID WINAPI CreateLineBitmaps(HBITMAP *paHbm) 
{ 
    HWND hwndDesktop = GetDesktopWindow(); 
    HDC hdcDesktop = GetDC(hwndDesktop); 
    HDC hdcMem = CreateCompatibleDC(hdcDesktop); 
    COLORREF clrMenu = GetSysColor(COLOR_MENU); 
    HBRUSH hbrOld; 
    HPEN hpenOld; 
    HBITMAP hbmOld; 
    int fnDrawMode; 
    int i; 
 
     // Create a brush using the menu background color, 
     // and select it into the memory DC. 
 
    hbrOld = SelectObject(hdcMem, CreateSolidBrush(clrMenu)); 
 
     // Create the bitmaps. Select each one into the memory 
     // DC that was created and draw in it. 
 
    for (i = 0; i < C_LINES; i++) 
    { 
        // Create the bitmap and select it into the DC. 
 
        paHbm[i] = CreateCompatibleBitmap(hdcDesktop, 
                CX_LINEBMP, CY_LINEBMP); 
        hbmOld = SelectObject(hdcMem, paHbm[i]); 
 
        // Fill the background using the brush. 
 
        PatBlt(hdcMem, 0, 0, CX_LINEBMP, CY_LINEBMP, PATCOPY); 
 
        // Create the pen and select it into the DC. 
 
        hpenOld = SelectObject(hdcMem, 
                CreatePen(PS_SOLID + i, 1, RGB(0, 0, 0))); 
 
         // Draw the line. To preserve the background color where 
         // the pen is white, use the R2_MASKPEN drawing mode. 
 
        fnDrawMode = SetROP2(hdcMem, R2_MASKPEN); 
        MoveToEx(hdcMem, 0, CY_LINEBMP / 2, NULL); 
        LineTo(hdcMem, CX_LINEBMP, CY_LINEBMP / 2); 
        SetROP2(hdcMem, fnDrawMode); 
 
        // Delete the pen, and select the old pen and bitmap. 
 
        DeleteObject(SelectObject(hdcMem, hpenOld)); 
        SelectObject(hdcMem, hbmOld); 
    } 
 
    // Delete the brush and select the original brush. 
 
    DeleteObject(SelectObject(hdcMem, hbrOld)); 
 
    // Delete the memory DC and release the desktop DC. 
 
    DeleteDC(hdcMem); 
    ReleaseDC(hwndDesktop, hdcDesktop); 
} 

Определяемая приложением функция AddBitmapMenu создает меню и добавляет в него указанное количество элементов меню растрового изображения. Затем он добавляет соответствующее имя меню в строку меню указанного окна.

VOID WINAPI AddBitmapMenu( 
        HWND hwnd,          // window that owned the menu bar 
        LPSTR lpszText,     // text of menu name on menu bar 
        UINT uID,           // ID of first bitmap menu item 
        HBITMAP *paHbm,     // bitmaps for the menu items 
        int cItems)         // number bitmap menu items 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup = CreatePopupMenu(); 
    MENUITEMINFO mii; 
    int i; 
 
    // Add the bitmap menu items to the menu. 
 
    for (i = 0; i < cItems; i++) 
    { 
        mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_DATA; 
        mii.wID = uID + i; 
        mii.hbmpItem = &paHbm[i]; 
        InsertMenuItem(hmenuPopup, i, TRUE, &mii); 
    } 
 
    // Add a menu name to the menu bar. 
 
    mii.fMask = MIIM_STRING | MIIM_DATA | MIIM_SUBMENU; 
    mii.fType = MFT_STRING; 
    mii.hSubMenu = hmenuPopup; 
    mii.dwTypeData = lpszText; 
    InsertMenuItem(hmenuBar, 
        GetMenuItemCount(hmenuBar), TRUE, &mii); 
} 

Создание элементов меню Owner-Drawn

Если вам нужен полный контроль над внешним видом элемента меню, вы можете использовать в приложении элемент меню, нарисованный владельцем. В этом разделе описаны шаги, связанные с созданием и использованием элемента меню, нарисованного владельцем.

Настройка флага Owner-Drawn

Невозможно определить элемент меню, нарисованный владельцем, в файле определения ресурса приложения. Вместо этого необходимо создать новый пункт меню или изменить существующий с помощью флага меню MFT_OWNERDRAW.

Вы можете использовать функцию InsertMenuItem или SetMenuItemInfo для указания элемента меню, нарисованного владельцем. Используйте InsertMenuItem, чтобы вставить новый пункт меню в указанной позиции в строке меню или меню. Используйте SetMenuItemInfo, чтобы изменить содержимое меню.

При вызове этих двух функций необходимо указать указатель на структуру MENUITEMINFO, которая указывает свойства нового элемента меню или свойства, которые необходимо изменить для существующего элемента меню. Чтобы сделать элемент нарисованным владельцем, укажите значение MIIM_FTYPE элемента fMask и значение MFT_OWNERDRAW для элемента fType.

Задав соответствующие члены структуры MENUITEMINFO, можно связать значение, определенное приложением, которое называется данные элемента, с каждым элементом меню. Для этого укажите значение MIIM_DATA для элемента fMask и определяемое приложением значение для элемента dwItemData.

Вы можете использовать данные элементов с любым типом элемента меню, но это особенно полезно для элементов, нарисованных владельцем. Например, предположим, что структура содержит сведения, используемые для рисования элемента меню. Приложение может использовать данные элемента меню для хранения указателя на структуру. Данные элемента отправляются в окно владельца меню с сообщениями WM_MEASUREITEM и WM_DRAWITEM. Чтобы получить данные элемента для меню в любое время, используйте функцию GetMenuItemInfo.

Приложения, написанные для более ранних версий системы, могут продолжать вызывать AppendMenu, InsertMenuили ModifyMenu, чтобы назначить флаг MF_OWNERDRAW элементу меню, нарисованному владельцем.

При вызове любого из этих трех функций можно передать значение в качестве параметра lpNewItem. Это значение может представлять любые сведения, значимые для приложения, и это будет доступно приложению при отображении элемента. Например, значение может содержать указатель на структуру; Структура, в свою очередь, может содержать текстовую строку и дескриптор логического шрифта, который будет использоваться для рисования строки.

меню Owner-Drawn и сообщение WM_MEASUREITEM

Перед первым отображением элемента меню, определяемого владельцем, система отправляет сообщение WM_MEASUREITEM в процедуру окна, которому принадлежит меню этого элемента. Это сообщение содержит указатель на структуру MEASUREITEMSTRUCT, которая идентифицирует элемент и содержит данные элемента, которые приложение могло ему присвоить. Процедура окна должна заполнить члены структуры itemWidth и itemHeight перед возвращением из обработки сообщения. Система использует сведения в этих элементах при создании ограничивающего прямоугольника, в котором приложение рисует элемент меню. Он также использует сведения для обнаружения того, когда пользователь выбирает элемент.

меню Owner-Drawn и сообщение WM_DRAWITEM

Каждый раз, когда элемент должен быть нарисован (например, при первом отображении или при его выборе), система отправляет сообщение WM_DRAWITEM в процедуру окна окна владельца меню. Это сообщение содержит указатель на структуру DRAWITEMSTRUCT, которая содержит сведения об элементе, включая данные элемента, которые могли быть назначены приложением. Кроме того, DRAWITEMSTRUCT содержит флаги, указывающие состояние элемента (например, серый или выбранный), а также ограничивающий прямоугольник и контекст устройства, который приложение использует для рисования элемента.

Приложение должно выполнять следующие действия при обработке сообщения WM_DRAWITEM:

  • Определите необходимый тип чертежа. Для этого проверьте элемент itemAction структуры DRAWITEMSTRUCT.
  • Нарисуйте элемент меню соответствующим образом, используя ограничивающий прямоугольник и контекст устройства, полученный из структуры DRAWITEMSTRUCT. Программе следует рисовать только в ограничивающем прямоугольнике. По соображениям производительности система не обрезает части изображения, рисуемые за пределами прямоугольника.
  • Восстановите все объекты GDI, выбранные для контекста устройства элемента меню.

Если пользователь выбирает элемент меню, система задает элемент itemAction элемента DRAWITEMSTRUCT значение ODA_SELECT и задает значение ODS_SELECTED в элементе itemState. Это подсказка приложения для перерисовки элемента меню, чтобы указать, что он выбран.

меню Owner-Drawn и сообщение WM_MENUCHAR

Меню, не созданные владельцем, могут указать мнемонику, вставив знак подчеркивания рядом с символом в строке меню. Это позволяет пользователю выбрать меню, нажав клавишу ALT и соответствующий мнемонический символ. Однако в меню с пользовательским оформлением нельзя указать мнемонику меню таким образом. Вместо этого приложение должно обработать сообщение WM_MENUCHAR для предоставления меню с собственным оформлением и мнемониками меню.

Сообщение WM_MENUCHAR отправляется, когда пользователь вводит мнемоник в меню, который не соответствует ни одному из предопределённых мнемоников текущего меню. Значение, содержащееся в wParam, указывает на символ ASCII, который соответствует клавише, нажатой пользователем вместе с клавишей ALT. Слово с низким порядком wParam указывает тип выбранного меню и может быть одним из следующих значений:

  • MF_POPUP, если текущее меню является подменю.
  • MF_SYSMENU, если меню является системным меню.

Слово высшего порядка wParam содержит дескриптор меню текущего меню. Окно с меню, нарисованным владельцем, может обрабатывать WM_MENUCHAR следующим образом:

   case WM_MENUCHAR:
      nIndex = Determine index of menu item to be selected from
               character that was typed and handle to the current
               menu.
      return MAKELRESULT(nIndex, 2);

Двойка в слове высокого порядка возвращаемого значения указывает системе, что слово низкого порядка возвращаемого значения содержит нулевой индекс элемента меню, который следует выбрать.

Следующие константы соответствуют возможным возвращаемым значениям из сообщения WM_MENUCHAR.

Постоянный Ценность Значение
MNC_IGNORE 0 Система должна отменить символ, нажатый пользователем, и создать короткий сигнал на системном динамике.
MNC_CLOSE 1 Система должна закрыть активное меню.
MNC_EXECUTE 2 Система должна выбрать элемент, указанный в младшем слове возвращаемого значения. Окно владельца получает сообщение WM_COMMAND.
MNC_SELECT 3 Система должна выбрать элемент, указанный в младшем слове возвращаемого значения.

 

Настройка шрифтов для Menu-Item текстовых строк

В этом разделе содержится пример из приложения, использующего элементы меню, нарисованные владельцем, в меню. В меню содержатся элементы, которые задают атрибуты текущего шрифта, а элементы отображаются с помощью соответствующего атрибута шрифта.

Вот как меню определяется в файле определения ресурса. Обратите внимание, что строки для пунктов меню "Обычный", "Полужирный", "Курсив" и "Подчеркивание" назначаются во время выполнения, поэтому в файле определения ресурсов их строки остаются пустыми.

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. Система использует значения ширины и высоты для вычисления размера меню.

Во время обработки сообщения WM_DRAWITEM строка элемента меню рисуется с пространством слева рядом со строкой для растрового изображения флажка. Если пользователь выбирает элемент, выбранный текст и цвет фона используются для рисования элемента.

LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
 
    typedef struct _MYITEM 
    { 
        HFONT hfont; 
        LPSTR psz; 
    } MYITEM;             // structure for item font and string  
 
    MYITEM *pmyitem;      // pointer to item's font and string        
    static MYITEM myitem[CITEMS];   // array of MYITEMS               
    static HMENU hmenu;             // handle to main menu            
    static COLORREF crSelText;  // text color of selected item        
    static COLORREF crSelBkgnd; // background color of selected item  
    COLORREF crText;            // text color of unselected item      
    COLORREF crBkgnd;           // background color unselected item   
    LPMEASUREITEMSTRUCT lpmis;  // pointer to item of data             
    LPDRAWITEMSTRUCT lpdis;     // pointer to item drawing data        
    HDC hdc;                    // handle to screen DC                
    SIZE size;                  // menu-item text extents             
    WORD wCheckX;               // check-mark width                   
    int nTextX;                 // width of menu item                 
    int nTextY;                 // height of menu item                
    int i;                      // loop counter                       
    HFONT hfontOld;             // handle to old font                 
    BOOL fSelected = FALSE;     // menu-item selection flag
    size_t * pcch;
    HRESULT hResult;           
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
 
            // Modify the Regular, Bold, Italic, and Underline 
            // menu items to make them owner-drawn items. Associate 
            // a MYITEM structure with each item to contain the 
            // string for and font handle to each item. 
 
            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]); 
 
            // Retrieve each item's font handle and copy it into 
            // the hfont member of each item's MYITEM structure. 
            // Also, copy each item's string into the structures. 
 
            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"; 
 
            // Retrieve the text and background colors of the 
            // selected menu text. 
 
            crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT); 
            crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT); 
 
            return 0; 
 
        case WM_MEASUREITEM: 
 
            // Retrieve a device context for the main window.  
 
            hdc = GetDC(hwnd); 
 
            // Retrieve pointers to the menu item's 
            // MEASUREITEMSTRUCT structure and MYITEM structure. 
 
            lpmis = (LPMEASUREITEMSTRUCT) lParam; 
            pmyitem = (MYITEM *) lpmis->itemData; 
 
            // Select the font associated with the item into 
            // the main window's device context. 
 
            hfontOld = SelectObject(hdc, pmyitem->hfont); 
 
            // Retrieve the width and height of the item's string, 
            // and then copy the width and height into the 
            // MEASUREITEMSTRUCT structure's itemWidth and 
            // itemHeight members.
            
            hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
            if (FAILED(hResult))
            {
            // Add code to fail as securely as possible.
                return;
            } 
 
            GetTextExtentPoint32(hdc, pmyitem->psz, 
                *pcch, &size); 
            lpmis->itemWidth = size.cx; 
            lpmis->itemHeight = size.cy; 
 
            // Select the old font back into the device context, 
            // and then release the device context. 
 
            SelectObject(hdc, hfontOld); 
            ReleaseDC(hwnd, hdc); 
 
            return TRUE; 
 
            break; 
 
        case WM_DRAWITEM: 
 
            // Get pointers to the menu item's DRAWITEMSTRUCT 
            // structure and MYITEM structure. 
 
            lpdis = (LPDRAWITEMSTRUCT) lParam; 
            pmyitem = (MYITEM *) lpdis->itemData; 
 
            // If the user has selected the item, use the selected 
            // text and background colors to display the item. 
 
            if (lpdis->itemState & ODS_SELECTED) 
            { 
                crText = SetTextColor(lpdis->hDC, crSelText); 
                crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd); 
                fSelected = TRUE; 
            } 
 
            // Remember to leave space in the menu item for the 
            // check-mark bitmap. Retrieve the width of the bitmap 
            // and add it to the width of the menu item. 
 
            wCheckX = GetSystemMetrics(SM_CXMENUCHECK); 
            nTextX = wCheckX + lpdis->rcItem.left; 
            nTextY = lpdis->rcItem.top; 
 
            // Select the font associated with the item into the 
            // item's device context, and then draw the string. 
 
            hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont);
            
            hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
            if (FAILED(hResult))
            {
            // Add code to fail as securely as possible.
                return;
            } 
 
            ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE, 
                &lpdis->rcItem, pmyitem->psz, 
                *pcch, NULL); 
 
            // Select the previous font back into the device 
            // context. 
 
            SelectObject(lpdis->hDC, hfontOld); 
 
            // Return the text and background colors to their 
            // normal state (not selected). 
 
            if (fSelected) 
            { 
                SetTextColor(lpdis->hDC, crText); 
                SetBkColor(lpdis->hDC, crBkgnd); 
            } 
 
            return TRUE; 
 
        // Process other messages.  
 
        case WM_DESTROY: 
 
            // Destroy the menu items' font handles.  
 
            for (i = 0; i < CITEMS; i++) 
                DeleteObject(myitem[i].hfont); 
 
            PostQuitMessage(0); 
            break; 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return NULL; 
} 
 
HFONT GetAFont(int fnFont) 
{ 
    static LOGFONT lf;  // structure for font information  
 
    // Get a handle to the ANSI fixed-pitch font, and copy 
    // information about the font to a LOGFONT structure. 
 
    GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT), 
        &lf); 
 
    // Set the font attributes, as appropriate.  
 
    if (fnFont == BOLD) 
        lf.lfWeight = FW_BOLD; 
    else 
        lf.lfWeight = FW_NORMAL; 
 
    lf.lfItalic = (fnFont == ITALIC); 
    lf.lfItalic = (fnFont == ULINE); 
 
    // Create the font, and then return its handle.  
 
    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); 
} 

Пример элементов меню Owner-Drawn

В примере в этом разделе используются элементы меню, нарисованные владельцем, в меню. Элементы меню выбирают определенные атрибуты шрифта, а приложение отображает каждый элемент меню с помощью шрифта, имеющего соответствующий атрибут. Например, элемент меню Italic отображается курсивным шрифтом. Название меню символов на строке меню открывает меню.

Панель меню и раскрывающееся меню изначально определяются ресурсом расширенного шаблона меню. Так как шаблон меню не может указывать элементы, нарисованные владельцем, изначально меню содержит четыре текстовых пункта меню со следующими строками: "Обычный", "Полужирный", "Курсив" и "Подчеркивание". Процедура окна приложения изменяет их на элементы, нарисованные владельцем, при обработке сообщения WM_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 для каждого элемента меню.

Ниже приведены соответствующие части файла заголовка приложения.

// Menu-item identifiers for the Character menu 
 
#define IDM_CHARACTER 10 
#define IDM_REGULAR   11 
#define IDM_BOLD      12 
#define IDM_ITALIC    13 
#define IDM_UNDERLINE 14 
 
// Structure associated with menu items 
 
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; 
 
        // Additional message processing goes here. 
 
        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; 
 
    // Get a handle to the pop-up menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Modify each menu item. Assume that the IDs IDM_REGULAR 
    // through IDM_UNDERLINE are consecutive numbers. 
 
    for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) 
    { 
         // Allocate an item structure, leaving space for a 
         // string of up to CCH_MAXITEMTEXT characters. 
 
        pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED, 
                sizeof(MYITEM) + CCH_MAXITEMTEXT); 
 
        // Save the item text in the item structure. 
 
        mii.fMask = MIIM_STRING; 
        mii.dwTypeData = pMyItem->szItemText; 
        mii.cch = CCH_MAXITEMTEXT; 
        GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
        pMyItem->cchItemText = mii.cch; 
 
        // Reallocate the structure to the minimum required size. 
 
        pMyItem = (MYITEM *) LocalReAlloc(pMyItem, 
                sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE); 
 
        // Create the font used to draw the item. 
 
        pMyItem->hfont = CreateMenuItemFont(uID); 
 
        // Change the item to an owner-drawn item, and save 
        // the address of the item structure as item data. 
 
        mii.fMask = MIIM_FTYPE | MIIM_DATA; 
        mii.fType = MFT_OWNERDRAW; 
        mii.dwItemData = (ULONG_PTR) pMyItem; 
        SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
    } 
    return TRUE; 
} 
 
HFONT CreateMenuItemFont(UINT uID) 
{ 
    LOGFONT lf;
    HRESULT hr; 
 
    ZeroMemory(&lf, sizeof(lf)); 
    lf.lfHeight = 20; 
    hr = StringCchCopy(lf.lfFaceName, 32, "Times New Roman");
    if (FAILED(hr))
    {
    // TODO: writer error handler
    } 
 
    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; 
 
    // Get a handle to the menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get  
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Free resources associated with each menu item. 
 
    for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) 
    { 
        // Get the item data. 
 
        mii.fMask = MIIM_DATA; 
        GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
        pMyItem = (MYITEM *) mii.dwItemData; 
 
        // Destroy the font and free the item structure. 
 
        DeleteObject(pMyItem->hfont); 
        LocalFree(pMyItem); 
    } 
} 
 
VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis) 
{ 
    MYITEM *pMyItem = (MYITEM *) lpmis->itemData; 
    HDC hdc = GetDC(hwnd); 
    HFONT hfntOld = (HFONT)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; 
 
    // Set the appropriate foreground and background colors. 
 
    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)); 
    } 
 
    // Determine where to draw and leave space for a check mark. 
 
    x = lpdis->rcItem.left; 
    y = lpdis->rcItem.top; 
    x += GetSystemMetrics(SM_CXMENUCHECK); 
 
    // Select the font and draw the text. 
 
    hfntPrev = (HFONT)SelectObject(lpdis->hDC, pMyItem->hfont); 
    ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE, 
            &lpdis->rcItem, pMyItem->szItemText, 
            pMyItem->cchItemText, NULL); 
 
    // Restore the original font and colors. 
 
    SelectObject(lpdis->hDC, hfntPrev); 
    SetTextColor(lpdis->hDC, clrPrevText); 
    SetBkColor(lpdis->hDC, clrPrevBkgnd); 
} 

Использование пользовательских растровых образов галочки

Система предоставляет значок галочки по умолчанию для отображения рядом с выбранным элементом меню. Вы можете настроить отдельный пункт меню, предоставив пару растровых картинок, чтобы заменить растровую картинку с галочкой по умолчанию. Система отображает одно растровое изображение при выборе элемента и другого, когда оно ясно. В этом разделе описаны шаги, связанные с созданием и использованием пользовательских растровых изображений галочки.

Создание пользовательских растровых изображений галочки

Пользовательский растровый рисунок флажка должен иметь тот же размер, что и по умолчанию точечный рисунок флажка. Размер битового изображения по умолчанию можно получить, вызвав функцию GetSystemMetrics. Слово с низким порядком возвращаемого значения этой функции указывает ширину; Слово высокого порядка указывает высоту.

Вы можете использовать ресурсы битмапов для предоставления растровых изображений галочек. Однако, поскольку требуемый размер растрового изображения зависит от типа отображения, может потребоваться изменить размер растрового изображения во время выполнения с помощью функции StretchBlt. В зависимости от растрового изображения искажение, вызванное изменением размера, может привести к неприемлемым результатам.

Вместо использования ресурса растрового изображения можно создать растровое изображение во время выполнения с помощью функций GDI.

Создание растрового изображения во время выполнения

  1. Используйте функцию CreateCompatibleDC, чтобы создать контекст устройства, совместимый с тем, который используется главным окном приложения.

    Параметр hdc функции может указывать либо NULL, либо возвращаемое значение функции. CreateCompatibleDC возвращает дескриптор в совместимый контекст устройства.

  2. Используйте функцию CreateCompatibleBitmap, чтобы создать растровое изображение, совместимое с основным окном приложения.

    Этот параметр nWidth и nHeight задает размер растрового изображения; они должны указывать сведения о ширине и высоте, возвращаемые функцией getSystemMetrics .

    Заметка

    Вы также можете использовать функцию createBitmapдля создания однохромной растровой карты.

     

  3. Используйте функцию SelectObject, чтобы выбрать растровое изображение в контексте совместимого устройства.

  4. Используйте функции рисования GDI, такие как Эллипс и LineTo, для рисования изображения в растровом рисунке или использования таких функций, как BitBlt и StretchBlt для копирования изображения в растровое изображение.

Для получения дополнительной информации см. растр изображений.

Связывание растровых карт с элементом меню

Вы связываете пару растровых изображений флажка с элементом меню путем передачи дескрипторов растровых изображений в функцию SetMenuItemBitmaps. Параметр hBitmapUnchecked определяет четкое растровое изображение, а параметр hBitmapChecked определяет выбранное растровое изображение. Если вы хотите удалить один или оба флажка из элемента меню, задайте параметр hBitmapUnchecked или hBitmapChecked, или оба, равными NULL.

Настройка атрибута check-mark

Функция CheckMenuItem задает атрибут флажка элемента меню выбранным или снятным. Можно указать значение MF_CHECKED, чтобы установить флажок в выбранное состояние, и значение MF_UNCHECKED, чтобы снять флажок.

Можно также задать состояние проверки элемента меню с помощью функции SetMenuItemInfo.

Иногда группа элементов меню представляет набор взаимоисключающих параметров. С помощью функции checkMenuRadioItemможно проверить один пункт меню, одновременно удаляя флажок из всех остальных элементов меню в группе.

Имитация флажков в меню

В этом разделе содержится пример, в котором показано, как имитировать флажки в меню. В примере содержится меню "Символ", элементы которого позволяют пользователю задавать атрибуты полужирного, курсива и подчеркивания текущего шрифта. Если атрибут шрифта действует, флажок отображается в флажке рядом с соответствующим элементом меню; В противном случае рядом с элементом отображается пустой флажок.

В примере значок галочки заменяется двумя растровыми изображениями: растровое изображение с выбранным флажком и растровое изображение с пустым полем. Выбранная растровая карта флажка отображается рядом с элементом меню полужирным шрифтом, курсивом или подчеркиванием, если для атрибута флажка элемента задано значение MF_CHECKED. Битовая карта пустого или незаполненного флажка отображается, если для атрибута флажка установлено значение MF_UNCHECKED.

Система предоставляет предопределенное растровое изображение, содержащее изображения, используемые для флажков и переключателей. Пример изолирует выбранные и пустые флажки, копирует их в две отдельные растровые карты, а затем использует их в качестве выбранных и очищенных растровых изображений для элементов в меню символов .

Чтобы получить дескриптор системного растрового изображения с флажками, в примере вызывается функция LoadBitmap, указывая NULL в качестве параметра hInstance и OBM_CHECKBOXES в качестве параметра lpBitmapName. Так как изображения в растровом изображении имеют одинаковый размер, пример может изолировать их, разделив ширину и высоту растрового изображения на количество изображений в строках и столбцах.

В следующей части файла определения ресурса показано, как определяются пункты в меню символов . Обратите внимание, что изначально атрибуты шрифта не применяются, поэтому для элемента Regular атрибут установлен как выбранный, а по умолчанию атрибут остальных элементов установлен как пустой.

#include "men3.h" 
 
MainMenu MENU 
BEGIN 
    POPUP   "&Character" 
    BEGIN 
        MENUITEM    "&Regular",     IDM_REGULAR, CHECKED 
        MENUITEM SEPARATOR 
        MENUITEM    "&Bold",        IDM_BOLD 
        MENUITEM    "&Italic",      IDM_ITALIC 
        MENUITEM    "&Underline",   IDM_ULINE 
    END 
END

Ниже приведено соответствующее содержимое файла заголовка приложения.

// Menu-item identifiers  
 
#define IDM_REGULAR 0x1 
#define IDM_BOLD    0x2 
#define IDM_ITALIC  0x4 
#define IDM_ULINE   0x8 
 
// Check-mark flags  
 
#define CHECK   1 
#define UNCHECK 2 
 
// Font-attribute mask  
 
#define ATTRIBMASK 0xe 
 
// Function prototypes  
 
LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM); 
HBITMAP GetMyCheckBitmaps(UINT); 
BYTE CheckOrUncheckMenuItem(BYTE, HMENU); 

В следующем примере показаны части процедуры окна, которые создают растровые изображения флажка, устанавливают атрибут флажка для элементов меню Полужирный, Курсиви Подчеркивание и уничтожают растровые изображения флажка.

LRESULT APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
 
    static HBITMAP hbmpCheck;   // handle to checked bitmap    
    static HBITMAP hbmpUncheck; // handle to unchecked bitmap  
    static HMENU hmenu;         // handle to main menu         
    BYTE fbFontAttrib;          // font-attribute flags        
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
 
            // Call the application-defined GetMyCheckBitmaps 
            // function to get the predefined checked and 
            // unchecked check box bitmaps. 
 
            hbmpCheck = GetMyCheckBitmaps(CHECK); 
            hbmpUncheck = GetMyCheckBitmaps(UNCHECK); 
 
            // Set the checked and unchecked bitmaps for the menu 
            // items. 
 
            hmenu = GetMenu(hwndMain); 
            SetMenuItemBitmaps(hmenu, IDM_BOLD, MF_BYCOMMAND, 
                hbmpUncheck, hbmpCheck); 
            SetMenuItemBitmaps(hmenu, IDM_ITALIC, MF_BYCOMMAND, 
                hbmpUncheck, hbmpCheck); 
            SetMenuItemBitmaps(hmenu, IDM_ULINE, MF_BYCOMMAND, 
                hbmpUncheck, hbmpCheck); 
 
            return 0; 
 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                // Process the menu commands.  
 
                case IDM_REGULAR: 
                case IDM_BOLD: 
                case IDM_ITALIC: 
                case IDM_ULINE: 
 
                    // CheckOrUncheckMenuItem is an application- 
                    // defined function that sets the menu item 
                    // checkmarks and returns the user-selected 
                    // font attributes. 
 
                    fbFontAttrib = CheckOrUncheckMenuItem( 
                        (BYTE) LOWORD(wParam), hmenu); 
 
                    // Set the font attributes.  
 
                    return 0; 
 
                // Process other command messages.  
 
                default: 
                    break; 
            } 
 
            break; 
 
        // Process other window messages.  
 
        case WM_DESTROY: 
 
            // Destroy the checked and unchecked bitmaps.  
 
            DeleteObject(hbmpCheck); 
            DeleteObject(hbmpUncheck); 
 
            PostQuitMessage(0); 
            break; 
 
        default: 
            return DefWindowProc(hwndMain, uMsg, wParam, lParam); 
    } 
    return NULL; 
} 
 
HBITMAP GetMyCheckBitmaps(UINT fuCheck) 
{ 
    COLORREF crBackground;  // background color                  
    HBRUSH hbrBackground;   // background brush                  
    HBRUSH hbrTargetOld;    // original background brush         
    HDC hdcSource;          // source device context             
    HDC hdcTarget;          // target device context             
    HBITMAP hbmpCheckboxes; // handle to check-box bitmap        
    BITMAP bmCheckbox;      // structure for bitmap data         
    HBITMAP hbmpSourceOld;  // handle to original source bitmap  
    HBITMAP hbmpTargetOld;  // handle to original target bitmap  
    HBITMAP hbmpCheck;      // handle to check-mark bitmap       
    RECT rc;                // rectangle for check-box bitmap    
    WORD wBitmapX;          // width of check-mark bitmap        
    WORD wBitmapY;          // height of check-mark bitmap       
 
    // Get the menu background color and create a solid brush 
    // with that color. 
 
    crBackground = GetSysColor(COLOR_MENU); 
    hbrBackground = CreateSolidBrush(crBackground); 
 
    // Create memory device contexts for the source and 
    // destination bitmaps. 
 
    hdcSource = CreateCompatibleDC((HDC) NULL); 
    hdcTarget = CreateCompatibleDC(hdcSource); 
 
    // Get the size of the system default check-mark bitmap and 
    // create a compatible bitmap of the same size. 
 
    wBitmapX = GetSystemMetrics(SM_CXMENUCHECK); 
    wBitmapY = GetSystemMetrics(SM_CYMENUCHECK); 
 
    hbmpCheck = CreateCompatibleBitmap(hdcSource, wBitmapX, 
        wBitmapY); 
 
    // Select the background brush and bitmap into the target DC. 
 
    hbrTargetOld = SelectObject(hdcTarget, hbrBackground); 
    hbmpTargetOld = SelectObject(hdcTarget, hbmpCheck); 
 
    // Use the selected brush to initialize the background color 
    // of the bitmap in the target device context. 
 
    PatBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY, PATCOPY); 
 
    // Load the predefined check box bitmaps and select it 
    // into the source DC. 
 
    hbmpCheckboxes = LoadBitmap((HINSTANCE) NULL, 
        (LPTSTR) OBM_CHECKBOXES); 
 
    hbmpSourceOld = SelectObject(hdcSource, hbmpCheckboxes); 
 
    // Fill a BITMAP structure with information about the 
    // check box bitmaps, and then find the upper-left corner of 
    // the unchecked check box or the checked check box. 
 
    GetObject(hbmpCheckboxes, sizeof(BITMAP), &bmCheckbox); 
 
    if (fuCheck == UNCHECK) 
    { 
        rc.left = 0; 
        rc.right = (bmCheckbox.bmWidth / 4); 
    } 
    else 
    { 
        rc.left = (bmCheckbox.bmWidth / 4); 
        rc.right = (bmCheckbox.bmWidth / 4) * 2; 
    } 
 
    rc.top = 0; 
    rc.bottom = (bmCheckbox.bmHeight / 3); 
 
    // Copy the appropriate bitmap into the target DC. If the 
    // check-box bitmap is larger than the default check-mark 
    // bitmap, use StretchBlt to make it fit; otherwise, just 
    // copy it. 
 
    if (((rc.right - rc.left) > (int) wBitmapX) || 
            ((rc.bottom - rc.top) > (int) wBitmapY)) 
    {
        StretchBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY, 
            hdcSource, rc.left, rc.top, rc.right - rc.left, 
            rc.bottom - rc.top, SRCCOPY); 
    }
 
    else 
    {
        BitBlt(hdcTarget, 0, 0, rc.right - rc.left, 
            rc.bottom - rc.top, 
            hdcSource, rc.left, rc.top, SRCCOPY); 
    }
 
    // Select the old source and destination bitmaps into the 
    // source and destination DCs, and then delete the DCs and 
    // the background brush. 
 
    SelectObject(hdcSource, hbmpSourceOld); 
    SelectObject(hdcTarget, hbrTargetOld); 
    hbmpCheck = SelectObject(hdcTarget, hbmpTargetOld); 
 
    DeleteObject(hbrBackground); 
    DeleteObject(hdcSource); 
    DeleteObject(hdcTarget); 
 
    // Return a handle to the new check-mark bitmap.  
 
    return hbmpCheck; 
} 
 
 
BYTE CheckOrUncheckMenuItem(BYTE bMenuItemID, HMENU hmenu) 
{ 
    DWORD fdwMenu; 
    static BYTE fbAttributes; 
 
    switch (bMenuItemID) 
    { 
        case IDM_REGULAR: 
 
            // Whenever the Regular menu item is selected, add a 
            // check mark to it and then remove checkmarks from 
            // any font-attribute menu items. 
 
            CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND | 
                MF_CHECKED); 
 
            if (fbAttributes & ATTRIBMASK) 
            { 
                CheckMenuItem(hmenu, IDM_BOLD, MF_BYCOMMAND | 
                    MF_UNCHECKED); 
                CheckMenuItem(hmenu, IDM_ITALIC, MF_BYCOMMAND | 
                    MF_UNCHECKED); 
                CheckMenuItem(hmenu, IDM_ULINE, MF_BYCOMMAND | 
                    MF_UNCHECKED); 
            } 
            fbAttributes = IDM_REGULAR; 
            return fbAttributes; 
 
        case IDM_BOLD: 
        case IDM_ITALIC: 
        case IDM_ULINE: 
 
            // Toggle the check mark for the selected menu item and 
            // set the font attribute flags appropriately. 
 
            fdwMenu = GetMenuState(hmenu, (UINT) bMenuItemID, 
                MF_BYCOMMAND); 
            if (!(fdwMenu & MF_CHECKED)) 
            { 
                CheckMenuItem(hmenu, (UINT) bMenuItemID, 
                    MF_BYCOMMAND | MF_CHECKED); 
                fbAttributes |= bMenuItemID; 
            }
            else 
            { 
                CheckMenuItem(hmenu, (UINT) bMenuItemID, 
                    MF_BYCOMMAND | MF_UNCHECKED); 
                fbAttributes ^= bMenuItemID; 
            } 
 
            // If any font attributes are currently selected, 
            // remove the check mark from the Regular menu item; 
            // if no attributes are selected, add a check mark 
            // to the Regular menu item. 
 
            if (fbAttributes & ATTRIBMASK) 
            { 
                CheckMenuItem(hmenu, IDM_REGULAR, 
                    MF_BYCOMMAND | MF_UNCHECKED); 
                fbAttributes &= (BYTE) ~IDM_REGULAR; 
            }
            else 
            { 
                CheckMenuItem(hmenu, IDM_REGULAR, 
                    MF_BYCOMMAND | MF_CHECKED); 
                fbAttributes = IDM_REGULAR; 
            } 
 
            return fbAttributes; 
    } 
} 

Пример использования пользовательских битмапов галочек

Пример в этом разделе назначает пользовательские растровые изображения флажка элементам меню в двух меню. Элементы меню в первом меню указывают свойства символов: полужирный шрифт, курсив и подчеркивание. Каждый пункт меню можно выбрать или очистить. Для этих элементов меню в примере используются растровые изображения флажка, похожие на выбранные и снятые состояния элемента управления флажка.

Элементы меню во втором меню указывают параметры выравнивания абзаца: слева, по центру и справа. В любое время выбирается только один из этих элементов меню. Для этих элементов меню в примере используются растровые изображения флажка, которые похожи на выбранные и четкие состояния элемента управления переключателем.

Процедура окна обрабатывает сообщение WM_CREATE путем вызова функции OnCreate, определенной приложением. OnCreate создает четыре растровых рисунка флажка, а затем назначает их соответствующим элементам меню с помощью функции SetMenuItemBitmaps.

Чтобы создать каждое растровое изображение, OnCreate вызывает определяемую приложением функцию CreateMenuBitmaps, указав указатель на функцию рисования для конкретного растрового изображения. CreateMenuBitmaps создает монохромное растровое изображение требуемого размера, помещает его в контекст устройства памяти и удаляет фон. Затем он вызывает указанную функцию рисования для заполнения переднего плана.

Четыре определяемые приложением функции рисования: DrawCheck, DrawUncheck, DrawRadioCheckи DrawRadioUncheck. Они рисуют прямоугольник с X внутри, пустой прямоугольник, эллипс, содержащий меньший заполненный эллипс, и пустой эллипс соответственно.

Процедура окна обрабатывает сообщение WM_DESTROY, удаляя битмапы с галочками. Он извлекает каждый дескриптор растрового изображения с помощью функции GetMenuItemInfo, а затем передает дескриптор функции.

Когда пользователь выбирает пункт меню, в окно владельца отправляется сообщение WM_COMMAND. Для элементов меню символ процедура окна вызывает функцию CheckCharacterItem, определяемую приложением. Для элементов в меню Абзац процедура окна вызывает определяемую приложением функцию CheckParagraphItem.

Каждый элемент в меню символ можно выбрать и очистить независимо. Поэтому CheckCharacterItem просто переключает состояние проверки указанного элемента меню. Сначала функция вызывает функцию GetMenuItemInfo, чтобы получить текущее состояние элемента меню. Затем он переключает флаг состояния MFS_CHECKED и задает новое состояние, вызвав функцию SetMenuItemInfo.

В отличие от атрибутов символов, можно выбрать только одно выравнивание абзаца одновременно. Таким образом, CheckParagraphItem проверяет указанный пункт меню и удаляет флажок из всех остальных элементов в меню. Для этого он вызывает функцию CheckMenuRadioItem.

Ниже приведены соответствующие части файла заголовка приложения.

// Menu-item identifiers for the Character menu 
 
#define IDM_CHARACTER 10 
#define IDM_BOLD      11 
#define IDM_ITALIC    12 
#define IDM_UNDERLINE 13 
 
// Menu-item identifiers for the Paragraph menu 
 
#define IDM_PARAGRAPH 20 
#define IDM_LEFT      21 
#define IDM_CENTER    22 
#define IDM_RIGHT     23 
 
// Function-pointer type for drawing functions 
 
typedef VOID (WINAPI * DRAWFUNC)(HDC hdc, SIZE size); 
 

Ниже приведены соответствующие части процедуры окна приложения и связанные функции.

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_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDM_BOLD: 
                case IDM_ITALIC: 
                case IDM_UNDERLINE: 
                    CheckCharacterItem(hwnd, LOWORD(wParam)); 
                    break; 
 
                case IDM_LEFT: 
                case IDM_CENTER: 
                case IDM_RIGHT: 
                    CheckParagraphItem(hwnd, LOWORD(wParam)); 
                    break; 
 
                // Process other commands here. 
 
            } 
            break; 
 
        // Process other messages here. 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return 0; 
} 
 
VOID WINAPI CheckCharacterItem(HWND hwnd, UINT uID) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
 
    // Get a handle to the Character menu. 
 
    mii.fMask = MIIM_SUBMENU;  // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Get the state of the specified menu item. 
 
    mii.fMask = MIIM_STATE;    // information to get 
    GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
 
    // Toggle the checked state. 
 
    mii.fState ^= MFS_CHECKED; 
    SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
} 
 
VOID WINAPI CheckParagraphItem(HWND hwnd, UINT uID) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
 
    // Get a handle to the Paragraph menu. 
 
    mii.fMask = MIIM_SUBMENU;  // information to get 
    GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Check the specified item and uncheck all the others. 
 
    CheckMenuRadioItem( 
            hmenuPopup,         // handle to menu 
            IDM_LEFT,           // first item in range 
            IDM_RIGHT,          // last item in range 
            uID,                // item to check 
            MF_BYCOMMAND        // IDs, not positions 
            ); 
} 
 
BOOL WINAPI OnCreate(HWND hwnd) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
    UINT uID; 
    HBITMAP hbmChecked; 
    HBITMAP hbmUnchecked; 
 
    // Get a handle to the Character menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Create the checked and unchecked bitmaps. 
 
    hbmChecked = CreateMenuBitmap(DrawCheck); 
    hbmUnchecked = CreateMenuBitmap(DrawUncheck); 
 
    // Set the check-mark bitmaps for each menu item. 
 
    for (uID = IDM_BOLD; uID <= IDM_UNDERLINE; uID++) 
    { 
        SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND, 
                hbmUnchecked, hbmChecked); 
    } 
 
    // Get a handle to the Paragraph pop-up menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Create the checked and unchecked bitmaps. 
 
    hbmChecked = CreateMenuBitmap(DrawRadioCheck); 
    hbmUnchecked = CreateMenuBitmap(DrawRadioUncheck); 
 
    // Set the check-mark bitmaps for each menu item. 
 
    for (uID = IDM_LEFT; uID <= IDM_RIGHT; uID++) 
    { 
        SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND, 
                hbmUnchecked, hbmChecked); 
    } 
 
    // Initially check the IDM_LEFT paragraph item. 
 
    CheckMenuRadioItem(hmenuPopup, IDM_LEFT, IDM_RIGHT, 
            IDM_LEFT, MF_BYCOMMAND); 
    return TRUE; 
} 
 
HBITMAP WINAPI CreateMenuBitmap(DRAWFUNC lpfnDraw) 
{ 
    // Create a DC compatible with the desktop window's DC. 
 
    HWND hwndDesktop = GetDesktopWindow(); 
    HDC hdcDesktop = GetDC(hwndDesktop); 
    HDC hdcMem = CreateCompatibleDC(hdcDesktop); 
 
    // Determine the required bitmap size. 
 
    SIZE size = { GetSystemMetrics(SM_CXMENUCHECK), 
                  GetSystemMetrics(SM_CYMENUCHECK) }; 
 
    // Create a monochrome bitmap and select it. 
 
    HBITMAP hbm = CreateBitmap(size.cx, size.cy, 1, 1, NULL); 
    HBITMAP hbmOld = SelectObject(hdcMem, hbm); 
 
    // Erase the background and call the drawing function. 
 
    PatBlt(hdcMem, 0, 0, size.cx, size.cy, WHITENESS); 
    (*lpfnDraw)(hdcMem, size); 
 
    // Clean up. 
 
    SelectObject(hdcMem, hbmOld); 
    DeleteDC(hdcMem); 
    ReleaseDC(hwndDesktop, hdcDesktop); 
    return hbm; 
} 
 
VOID WINAPI DrawCheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Rectangle(hdc, 0, 0, size.cx, size.cy); 
    MoveToEx(hdc, 0, 0, NULL); 
    LineTo(hdc, size.cx, size.cy); 
    MoveToEx(hdc, 0, size.cy - 1, NULL); 
    LineTo(hdc, size.cx - 1, 0); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI DrawUncheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Rectangle(hdc, 0, 0, size.cx, size.cy); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI DrawRadioCheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Ellipse(hdc, 0, 0, size.cx, size.cy); 
    SelectObject(hdc, GetStockObject(BLACK_BRUSH)); 
    Ellipse(hdc, 2, 2, size.cx - 2, size.cy - 2); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI DrawRadioUncheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Ellipse(hdc, 0, 0, size.cx, size.cy); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI OnDestroy(HWND hwnd) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
 
    // Get a handle to the Character menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Get the check-mark bitmaps and delete them. 
 
    mii.fMask = MIIM_CHECKMARKS; 
    GetMenuItemInfo(hmenuPopup, IDM_BOLD, FALSE, &mii); 
    DeleteObject(mii.hbmpChecked); 
    DeleteObject(mii.hbmpUnchecked); 
 
    // Get a handle to the Paragraph menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Get the check-mark bitmaps and delete them. 
 
    mii.fMask = MIIM_CHECKMARKS; 
    GetMenuItemInfo(hmenuPopup, IDM_LEFT, FALSE, &mii); 
    DeleteObject(mii.hbmpChecked); 
    DeleteObject(mii.hbmpUnchecked); 
}