Udostępnij przez


Korzystanie z menu

W tej sekcji opisano następujące zadania:

Korzystanie z zasobu Menu-Template

Zazwyczaj w aplikacji dodajesz menu, tworząc zasób szablonu menu, a następnie wczytując je w trakcie działania programu. W tej sekcji opisano format szablonu menu i wyjaśniono, jak załadować zasób szablonu menu i używać go w aplikacji. Aby uzyskać informacje na temat tworzenia zasobu szablonu menu, zapoznaj się z dokumentacją zawartą w narzędziach programistycznych.

Rozszerzony format Menu-Template

Rozszerzony format szablonu menu obsługuje dodatkowe funkcje menu. Podobnie jak standardowe zasoby szablonu menu, rozszerzone zasoby szablonu menu mają typ zasobu RT_MENU. System rozróżnia dwa formaty zasobów według numeru wersji, który jest pierwszym elementem członkowskim nagłówka zasobu.

Szablon menu rozszerzonego składa się z struktury MENUEX_TEMPLATE_HEADER, po której następuje jeszcze jedna struktura definicji elementów MENUEX_TEMPLATE_ITEM.

Stary format Menu-Template

Stary szablon menu (system Microsoft Windows NT 3.51 i starsze) definiuje menu, ale nie obsługuje nowych funkcji menu. Stary zasób szablonu menu ma typ zasobu RT_MENU.

Stary szablon menu składa się ze struktury MENUITEMTEMPLATEHEADER, po której następuje co najmniej jedna struktura MENUITEMTEMPLATE.

Ładowanie zasobu Menu-Template

Aby załadować zasób szablonu menu, użyj funkcji LoadMenu, określając dojście do modułu zawierającego zasób i identyfikator szablonu menu. Funkcja LoadMenu zwraca uchwyt menu, którego można użyć do przypisania menu do okna. To okno staje się oknem właściciela menu, odbierając wszystkie komunikaty wygenerowane przez menu.

Aby utworzyć menu na podstawie szablonu menu, który jest już w pamięci, użyj funkcji LoadMenuIndirect. Jest to przydatne, jeśli aplikacja dynamicznie generuje szablony menu.

Aby przypisać menu do okna, użyj funkcji SetMenu lub określ uchwyt menu w parametrze hMenu funkcji CreateWindowEx podczas tworzenia okna. Innym sposobem przypisania menu do okna jest określenie szablonu menu podczas rejestrowania klasy okna; szablon identyfikuje określone menu jako menu klasy dla tej klasy.

Aby system automatycznie przypisywać określone menu do okna, określ szablon menu podczas rejestrowania klasy okna. Szablon identyfikuje określone menu jako menu klasy dla tej klasy okna. Następnie podczas tworzenia okna określonej klasy system automatycznie przypisuje określone menu do okna.

Nie można przypisać menu do okna, które jest oknem podrzędnym.

Aby utworzyć menu klasy, dołącz identyfikator zasobu szablonu menu jako lpszMenuName członek struktury WNDCLASS, a następnie przekaż wskaźnik do tej struktury funkcji RegisterClass.

Tworzenie menu klasy

W poniższym przykładzie pokazano, jak utworzyć menu klasowe dla aplikacji, jak utworzyć okno korzystające z tego menu i jak przetwarzać polecenia menu w procedurze okna.

Poniżej znajduje się odpowiednia część pliku nagłówka aplikacji:

// Menu-template resource identifier 
 
#define IDM_MYMENURESOURCE   3

Poniżej przedstawiono odpowiednie części samej aplikacji:

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; 
} 

Tworzenie menu skrótów

Aby użyć menu skrótów w aplikacji, przekaż uchwyt do funkcji TrackPopupMenuEx. Aplikacja zazwyczaj wywołuje TrackPopupMenuEx w procedurze okna w odpowiedzi na wiadomość wygenerowaną przez użytkownika, taką jak WM_LBUTTONDOWN lub WM_KEYDOWN.

Oprócz uchwytu menu podręcznego TrackPopupMenuEx wymaga określenia uchwytu w oknie właściciela, położenia menu skrótów (we współrzędnych ekranu) i przycisku myszy, którego użytkownik może użyć do wybrania elementu.

Starsza funkcja TrackPopupMenu jest nadal obsługiwana, ale nowe aplikacje powinny używać funkcji TrackPopupMenuEx. Funkcja TrackPopupMenuEx wymaga tych samych parametrów co TrackPopupMenu, ale umożliwia również określenie części ekranu, której menu nie powinno być niejasne. Aplikacja zwykle wywołuje te funkcje w procedurze okna podczas przetwarzania komunikatu WM_CONTEXTMENU.

Możesz określić położenie menu skrótów, podając współrzędne x i y wraz z flagą TPM_CENTERALIGN, TPM_LEFTALIGNlub TPM_RIGHTALIGN. Flaga określa położenie menu skrótów względem współrzędnych x i y.

Użytkownik powinien zezwolić użytkownikowi na wybranie elementu z menu skrótów przy użyciu tego samego przycisku myszy używanego do wyświetlania menu. W tym celu określ flagę TPM_LEFTBUTTON lub TPM_RIGHTBUTTON, aby wskazać przycisk myszy, którego użytkownik może użyć do wybrania elementu menu.

Przetwarzanie komunikatu WM_CONTEXTMENU

Komunikat WM_CONTEXTMENU jest generowany, gdy procedura okna aplikacji przekazuje komunikat WM_RBUTTONUP lub WM_NCRBUTTONUP do funkcji DefWindowProc. Aplikacja może przetworzyć ten komunikat, aby wyświetlić menu skrótów odpowiednie dla określonej części ekranu. Jeśli aplikacja nie wyświetla menu skrótów, powinien przekazać komunikat do DefWindowProc do obsługi domyślnej.

Poniżej przedstawiono przykład przetwarzania komunikatów WM_CONTEXTMENU, który może pojawić się w procedurze okna aplikacji. Wyrazy o niskiej kolejności i wysokiej kolejności lParam określają współrzędne ekranu myszy po zwolnieniu prawego przycisku myszy (należy pamiętać, że te współrzędne mogą przyjmować wartości ujemne w systemach z wieloma monitorami). Funkcja OnContextMenu zdefiniowana przez aplikację zwraca true, jeśli wyświetla menu kontekstowe lub false, jeśli nie.

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

Następująca funkcja OnContextMenu zdefiniowana przez aplikację wyświetla menu skrótów, jeśli określona pozycja myszy znajduje się w obszarze klienta okna. Bardziej zaawansowana funkcja może wyświetlać jedno z kilku różnych menu, w zależności od tego, która część obszaru klienta jest określona. Aby wyświetlić menu skrótów, ten przykład wywołuje funkcję zdefiniowaną przez aplikację o nazwie DisplayContextMenu. Aby uzyskać opis tej funkcji, zobacz Wyświetlanie menu skrótów.

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; 
} 

Tworzenie skrótu menu Font-Attributes

Przykład w tej sekcji zawiera fragmenty kodu z aplikacji, która tworzy i wyświetla menu skrótów, które umożliwia użytkownikowi ustawianie czcionek i atrybutów czcionek. Aplikacja wyświetla menu w obszarze klienta okna głównego za każdym razem, gdy użytkownik kliknie lewy przycisk myszy.

Oto szablon menu kontekstowego, który znajduje się w pliku definicji zasobów aplikacji.

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 

Poniższy przykład przedstawia procedurę okna i funkcje pomocnicze używane do tworzenia i wyświetlania menu skrótów.

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); 
} 

Wyświetlanie menu skrótów

Funkcja pokazana w poniższym przykładzie wyświetla menu skrótów.

Aplikacja zawiera zasób menu zidentyfikowany przez ciąg "ShortcutExample". Pasek menu zawiera po prostu nazwę menu. Aplikacja używa funkcji TrackPopupMenu, aby wyświetlić menu skojarzone z tym elementem menu. (Pasek menu nie jest wyświetlany, ponieważ TrackPopupMenu wymaga dojścia do menu, podmenu lub menu skrótów).

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); 
} 

Korzystanie z map bitowych Menu-Item

System może używać mapy bitowej zamiast ciągu tekstowego do wyświetlania elementu menu. Aby użyć mapy bitowej, należy ustawić flagę MIIM_BITMAP dla elementu menu i określić uchwyt mapy bitowej, który system ma wyświetlić dla elementu menu w hbmpItem członie MENUITEMINFO struktury. W tej sekcji opisano sposób używania map bitowych dla elementów menu.

Ustawianie flagi typu mapy bitowej

Flaga MIIM_BITMAP lub MF_BITMAP nakazuje systemowi użycie mapy bitowej, a nie ciągu tekstowego w celu wyświetlenia elementu menu. Flagi MIIM_BITMAP lub MF_BITMAP dla elementu menu muszą być ustawione podczas wykonywania programu; nie można ich ustawić w pliku definicji zasobów.

W przypadku nowych aplikacji można użyć funkcji SetMenuItemInfo lub InsertMenuItem, aby ustawić flagę typu MIIM_BITMAP. Aby zmienić element menu z elementu tekstowego na element mapy bitowej, użyj SetMenuItemInfo. Aby dodać nowy element mapy bitowej do menu, użyj funkcji InsertMenuItem.

Aplikacje napisane we wcześniejszych wersjach systemu mogą nadal używać funkcji ModifyMenu, InsertMenulub AppendMenu, aby ustawić flagę MF_BITMAP. Aby zmienić element menu z elementu ciągu tekstowego na element mapy bitowej, użyj ModifyMenu. Aby dodać nowy element mapy bitowej do menu, użyj flagi MF_BITMAP z funkcją InsertMenu lub AppendMenu.

Tworzenie mapy bitowej

Po ustawieniu flagi typu MIIM_BITMAP lub MF_BITMAP dla elementu menu należy również określić uchwyt do mapy bitowej, którą system ma wyświetlić dla elementu menu. Możesz podać mapę bitową jako zasób mapy bitowej lub utworzyć mapę bitową w czasie wykonywania. Jeśli używasz zasobu mapy bitowej, możesz użyć funkcji LoadBitmap, aby załadować mapę bitową i uzyskać jej uchwyt.

Aby utworzyć mapę bitową w czasie wykonywania, użyj funkcji interfejsu urządzenia graficznego systemu Windows (GDI). Interfejs GDI udostępnia kilka sposobów tworzenia mapy bitowej w czasie wykonywania, ale deweloperzy zazwyczaj używają następującej metody:

  1. Użyj funkcji CreateCompatibleDC, aby utworzyć kontekst urządzenia zgodny z kontekstem urządzenia używanym przez główne okno aplikacji.
  2. Użyj funkcji CreateCompatibleBitmap, aby utworzyć mapę bitową zgodną z głównym oknem aplikacji lub użyć funkcji CreateBitmap, aby utworzyć monochromatyczną mapę bitową.
  3. Użyj funkcji SelectObject, aby wybrać mapę bitową w zgodnym kontekście urządzenia.
  4. Użyj funkcji rysowania GDI, takich jak Ellipse i LineTo, aby narysować obraz do mapy bitowej.

Aby uzyskać więcej informacji, zobacz Mapy bitowe.

Dodawanie linii i wykresów do menu

Poniższy przykładowy kod pokazuje, jak utworzyć menu zawierające mapy bitowe elementu menu. Tworzy dwa menu. Pierwszy to menu Wykres, które zawiera trzy mapy bitowe elementów menu: wykres kołowy, wykres liniowy i wykres słupkowy. W przykładzie pokazano, jak załadować te mapy bitowe z pliku zasobów aplikacji, a następnie użyć funkcji CreatePopupMenu i AppendMenu do utworzenia menu i elementów menu.

Drugie menu to menu Linie. Zawiera bitmapy przedstawiające style linii dostarczone przez predefiniowane pióro w systemie. Mapy bitowe w stylu liniowym są tworzone w czasie wykonywania przy użyciu funkcji GDI.

Poniżej przedstawiono definicje zasobów mapy bitowej w pliku definicji zasobów aplikacji.

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

Poniżej przedstawiono odpowiednie fragmenty pliku nagłówka aplikacji.

// 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); 

W poniższym przykładzie pokazano, jak w aplikacji są tworzone menu i mapy bitowe elementów menu.

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; 
} 

Przykład map bitowych Menu-Item

W przykładzie w tym temacie są tworzone dwa menu, z których każdy zawiera kilka elementów menu mapy bitowej. Dla każdego menu aplikacja dodaje odpowiednią nazwę menu na pasku menu głównego okna.

Pierwsze menu zawiera elementy menu pokazujące każdy z trzech typów wykresów: kołowy, liniowy i słupkowy. Mapy bitowe dla tych elementów menu są definiowane jako zasoby i ładowane przy użyciu funkcji LoadBitmap. Z tym menu skojarzone jest menu o nazwie "Wykres" umieszczone na pasku menu.

Drugie menu zawiera elementy menu z pięcioma stylami linii używanymi z funkcją CreatePen: PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOTi PS_DASHDOTDOT. Aplikacja tworzy mapy bitowe dla tych elementów menu podczas działania przy użyciu funkcji rysujących GDI. Na pasku menu znajduje się nazwa menu Lines powiązana z tym menu.

W procedurze okna aplikacji zdefiniowane są dwie statyczne tablice uchwytów map bitowych. Jedna tablica zawiera uchwyty trzech map bitowych używanych w menu Chart. Drugi zawiera uchwyty pięciu map bitowych używanych do menu Lines. Podczas przetwarzania komunikatu WM_CREATE procedura okna ładuje mapy bitowe wykresu, tworzy mapy bitowe linii, a następnie dodaje odpowiednie elementy menu. Podczas przetwarzania komunikatu WM_DESTROY procedura okna usuwa wszystkie mapy bitowe.

Poniżej przedstawiono odpowiednie fragmenty pliku nagłówka aplikacji.

// 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 

Odpowiednie części procedury okna są przedstawione poniżej. Procedura okna wykonuje większość jego inicjowania, wywołując zdefiniowane przez aplikację elementy LoadChartBitmaps, CreateLineBitmaps i AddBitmapMenu, opisane w dalszej części tego tematu.

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; 
} 

Funkcja LoadChartBitmaps zdefiniowana przez aplikację ładuje zasoby mapy bitowej dla menu wykresu, wywołując funkcję LoadBitmap w następujący sposób.

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)); 
} 

Funkcja CreateLineBitmaps zdefiniowana przez aplikację tworzy mapy bitowe dla menu Linie przy użyciu funkcji rysunku GDI. Funkcja tworzy kontekst urządzenia pamięci (DC) z takimi samymi właściwościami jak kontekst urządzenia okna pulpitu. Dla każdego stylu linii funkcja tworzy mapę bitową, wybiera ją do pamięci DC i rysuje na niej.

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); 
} 

Funkcja AddBitmapMenu zdefiniowana przez aplikację tworzy menu i dodaje do niej określoną liczbę elementów menu mapy bitowej. Następnie dodaje odpowiednią nazwę menu na pasku menu określonego okna.

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); 
} 

Tworzenie elementów menu Owner-Drawn

Jeśli potrzebujesz pełnej kontroli nad wyglądem elementu menu, możesz użyć elementu menu rysowanego przez właściciela w aplikacji. W tej sekcji opisano kroki związane z tworzeniem i używaniem elementu menu rysowanego przez właściciela.

Ustawianie flagi Owner-Drawn

Nie można zdefiniować elementu menu rysowanego przez właściciela w pliku definicji zasobów aplikacji. Zamiast tego należy utworzyć nowy element menu lub zmodyfikować istniejący przy użyciu flagi menu MFT_OWNERDRAW.

Możesz użyć funkcji InsertMenuItem lub SetMenuItemInfo, aby określić element menu rysowany przez właściciela. Użyj InsertMenuItem, aby wstawić nowy element menu w określonej pozycji na pasku menu lub menu. Użyj SetMenuItemInfo, aby zmienić zawartość menu.

Podczas wywoływania tych dwóch funkcji należy określić wskaźnik do MENUITEMINFO struktury, która określa właściwości nowego elementu menu lub właściwości, które chcesz zmienić dla istniejącego elementu menu. Aby ustawić element rysowany przez właściciela, określ wartość MIIM_FTYPE dla członka fMask i wartość MFT_OWNERDRAW dla członka fType.

Ustawiając odpowiednie elementy członkowskie struktury MENUITEMINFO, można skojarzyć wartość zdefiniowaną przez aplikację, która jest nazywana danymi elementu , z każdym elementem menu. W tym celu określ wartość MIIM_DATA elementu członkowskiego fMask oraz wartość zdefiniowaną przez aplikację dla elementu członkowskiego dwItemData.

Dane elementów można używać z dowolnym typem elementu menu, ale jest szczególnie przydatne w przypadku elementów rysowanych przez właściciela. Załóżmy na przykład, że struktura zawiera informacje używane do rysowania elementu menu. Aplikacja może używać danych elementu dla pozycji menu do przechowywania wskaźnika do struktury. Dane elementu są wysyłane do okna właściciela menu z komunikatami WM_MEASUREITEM i WM_DRAWITEM. Aby pobrać dane elementu dla menu w dowolnym momencie, użyj funkcji GetMenuItemInfo.

Aplikacje napisane we wcześniejszych wersjach systemu mogą nadal wywoływać AppendMenu, InsertMenulub ModifyMenu, aby przypisać flagę MF_OWNERDRAW do elementu menu rysowanego przez właściciela.

Podczas wywoływania dowolnej z tych trzech funkcji można przekazać wartość jako parametr lpNewItem. Ta wartość może reprezentować wszelkie informacje istotne dla aplikacji i będą dostępne dla aplikacji, gdy element ma być wyświetlany. Na przykład wartość może zawierać wskaźnik do struktury; z kolei struktura może zawierać ciąg tekstowy i uchwyt do czcionki logicznej, która będzie używana przez aplikację do rysowania ciągu.

Owner-Drawn Menu oraz komunikat WM_MEASUREITEM

Zanim system wyświetli element menu rysowany przez właściciela po raz pierwszy, wysyła komunikat WM_MEASUREITEM do procedury okna, które jest właścicielem menu elementu. Ten komunikat zawiera wskaźnik do struktury MEASUREITEMSTRUCT, która identyfikuje element i zawiera dane elementu, które aplikacja mogła przypisać do niego. Procedura okna musi wypełnić składowe struktury itemWidth i itemHeight przed powrotem z przetwarzania komunikatu. System używa informacji w tych elementach podczas tworzenia prostokąta obwiedni, w którym aplikacja rysuje element menu. Używa również informacji do wykrywania, kiedy użytkownik wybierze element.

Owner-Drawn Menu i komunikat WM_DRAWITEM

Za każdym razem, gdy element musi zostać narysowany (na przykład po pierwszym wyświetleniu lub wybraniu go przez użytkownika), system wysyła komunikat WM_DRAWITEM do procedury okna okna właściciela menu. Ten komunikat zawiera wskaźnik do struktury DRAWITEMSTRUCT, która zawiera informacje o elemencie, w tym dane przypisane do elementu przez aplikację. Ponadto DRAWITEMSTRUCT zawiera flagi wskazujące stan elementu (na przykład szary lub zaznaczony), a także prostokąt ograniczenia i kontekst urządzenia używany przez aplikację do rysowania elementu.

Aplikacja musi wykonać następujące czynności podczas przetwarzania komunikatu WM_DRAWITEM:

  • Określ wymagany typ rysunku. W tym celu sprawdź element Akcja składową struktury DRAWITEMSTRUCT.
  • Narysuj odpowiednio element menu przy użyciu prostokąta granicznego i kontekstu urządzenia uzyskanego ze struktury DRAWITEMSTRUCT. Aplikacja musi rysować tylko w obrębie prostokąta granicznego. Ze względu na wydajność system nie wycina fragmentów obrazu, które są rysowane poza prostokątem.
  • Przywróć wszystkie obiekty GDI wybrane dla kontekstu urządzenia elementu menu.

Jeśli użytkownik wybierze element menu, system ustawia element itemAction składowej DRAWITEMSTRUCT na wartość ODA_SELECT oraz ustawia wartość ODS_SELECTED w elemencie itemState. To sygnał dla aplikacji, aby ponownie wyrysować element menu, aby wskazać, że jest zaznaczony.

Menu Owner-Drawn i komunikat WM_MENUCHAR

Menu inne niż rysowane przez użytkownika mogą określać skrót klawiaturowy dla menu, poprzez umieszczenie podkreślenia obok znaku w napisie menu. Dzięki temu użytkownik może wybrać menu, naciskając ALT i naciskając znak mnemonic menu. W menu rysowanym przez aplikację nie można jednak w ten sposób określić mnemonika. Zamiast tego aplikacja musi przetworzyć komunikat WM_MENUCHAR, aby udostępnić menu rysowane przez użytkownika z mnemonikami menu.

Komunikat WM_MENUCHAR jest wysyłany, gdy użytkownik wpisze skrót klawiszowy menu, który nie odpowiada żadnemu z wcześniej zdefiniowanych skrótów klawiszowych bieżącego menu. Wartość zawarta w wParam określa znak ASCII, który odpowiada kluczowi, który użytkownik nacisnął za pomocą ALT. Wyraz o niskiej kolejności wParam określa typ wybranego menu i może być jedną z następujących wartości:

  • MF_POPUP, jeśli bieżące menu jest podmenu.
  • MF_SYSMENU, jeśli menu to menu systemowe.

Słowo wyższego rzędu wParam zawiera uchwyt bieżącego menu. Okno z menu narysowanymi przez właściciela może przetwarzać WM_MENUCHAR w następujący sposób:

   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);

Dwa wyrazy o wysokiej kolejności zwracanej wartości informują system, że słowo zwracane o niskiej kolejności zawiera indeks zerowy elementu menu do wybrania.

Następujące stałe odpowiadają możliwym wartościom zwracanych z komunikatu WM_MENUCHAR.

Stały Wartość Znaczenie
MNC_IGNORE 0 System powinien odrzucić znak naciśnięty przez użytkownika i utworzyć krótki sygnał dźwiękowy na głośniku systemowym.
MNC_CLOSE 1 System powinien zamknąć aktywne menu.
MNC_EXECUTE 2 System powinien wybrać element określony w wyrazie o niskiej kolejności wartości zwracanej. Okno właściciela odbiera komunikat WM_COMMAND.
MNC_SELECT 3 System powinien wybrać element określony w słowie o niskiej kolejności wartości zwracanej.

 

Ustawianie czcionek dla ciągów tekstowych Menu-Item

Ta sekcja zawiera przykład aplikacji, która używa własnoręcznie rysowanych elementów menu w menu. Menu zawiera elementy, które ustawiają atrybuty bieżącej czcionki, a elementy są wyświetlane przy użyciu odpowiedniego atrybutu czcionki.

Poniżej przedstawiono sposób definiowania menu w pliku definicji zasobów. Należy pamiętać, że ciągi dla elementów menu Normalne, Pogrubienie, Kursywa i Podkreślenie są przypisywane w czasie działania programu, więc w pliku definicji zasobów są puste.

MainMenu MENU 
BEGIN 
    POPUP   "&Character" 
    BEGIN 
        MENUITEM    "",    IDM_REGULAR 
        MENUITEM SEPARATOR 
        MENUITEM    "",    IDM_BOLD 
        MENUITEM    "",    IDM_ITALIC 
        MENUITEM    "",    IDM_ULINE 
    END 
END 

Procedura okna aplikacji przetwarza komunikaty związane z używaniem elementów menu definiowanych przez właściciela. Aplikacja używa komunikatu WM_CREATE, aby wykonać następujące czynności:

  • Ustaw flagę MF_OWNERDRAW dla elementów menu.
  • Ustaw ciągi tekstowe dla elementów menu.
  • Uzyskaj uchwyty czcionek używanych do rysowania elementów.
  • Uzyskaj wartości koloru tekstu i tła dla wybranych elementów menu.

Ciągi tekstowe i uchwyty czcionek są przechowywane w tablicy struktur MYITEM zdefiniowanych przez aplikację. Funkcja GetAFont zdefiniowana przez aplikację tworzy czcionkę odpowiadającą określonemu atrybutowi czcionki i zwraca uchwyt do czcionki. Uchwyty są niszczone podczas przetwarzania komunikatu WM_DESTROY.

Podczas przetwarzania komunikatu WM_MEASUREITEM przykład pobiera szerokość i wysokość ciągu elementu menu i kopiuje te wartości do struktury MEASUREITEMSTRUCT. System oblicza rozmiar menu przy użyciu wartości szerokości i wysokości.

Podczas przetwarzania komunikatu WM_DRAWITEM ciąg elementu menu jest rysowany, zostawiając miejsce obok niego na bitową mapę znacznika wyboru. Jeśli użytkownik wybierze element, wybrane kolory tekstu i tła zostaną użyte do narysowania elementu.

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); 
} 

Przykład elementów menu Owner-Drawn

W przykładzie w tym temacie są używane elementy menu rysowane przez właściciela w menu. Elementy menu wybierają określone atrybuty czcionki, a aplikacja wyświetla każdy element menu przy użyciu czcionki, która ma odpowiedni atrybut. Na przykład element menu kursywa jest wyświetlany czcionką kursywy. Nazwa menu znaku na pasku menu otwiera menu.

Pasek menu i menu rozwijane są definiowane początkowo przez rozszerzony zasób szablonu menu. Ponieważ szablon menu nie może określić elementów rysowanych przez właściciela, menu początkowo zawiera cztery elementy menu tekstowego z następującymi ciągami: "Regular", "Bold", "Italic" i "Underline". Procedura okna aplikacji zmienia je na elementy rysowane przez właściciela, gdy przetwarza komunikat WM_CREATE. Po odebraniu komunikatu WM_CREATE procedura okna wywołuje funkcję OnCreate zdefiniowaną przez aplikację, która wykonuje następujące kroki dla każdego elementu menu:

  • Przydziela strukturę MYITEM zdefiniowaną przez aplikację.
  • Pobiera tekst elementu menu i zapisuje go w strukturze MYITEM zdefiniowanej przez aplikację.
  • Tworzy font używany do wyświetlania elementu menu i zapisuje jego uchwyt w strukturze MYITEM określonej przez aplikację.
  • Zmienia typ elementu menu na MFT_OWNERDRAW i zapisuje wskaźnik w strukturze MYITEM zdefiniowanej przez aplikację jako dane elementu.

Ponieważ wskaźnik do każdej struktury MYITEM zdefiniowanej przez aplikację jest zapisywany jako dane elementu, jest przekazywany do procedury okna wraz z komunikatem WM_MEASUREITEM oraz WM_DRAWITEM dla odpowiedniego elementu menu. Wskaźnik jest zawarty w elemencie członkowskim itemData zarówno w strukturze MEASUREITEMSTRUCT, jak i w strukturze DRAWITEMSTRUCT.

Komunikat WM_MEASUREITEM jest wysyłany dla każdego elementu menu rysowanego przez właściciela przy pierwszym wyświetleniu. Aplikacja przetwarza ten komunikat, wybierając czcionkę elementu menu w kontekście urządzenia, a następnie określając miejsce wymagane do wyświetlenia tekstu elementu menu w tej czcionki. Tekst czcionki i elementu menu są określane przez strukturę MYITEM elementu menu (strukturę zdefiniowaną przez aplikację). Aplikacja określa rozmiar tekstu przy użyciu funkcji GetTextExtentPoint32.

Procedura okna przetwarza komunikat WM_DRAWITEM, wyświetlając tekst elementu menu w odpowiedniej czcionki. Tekst czcionki oraz tekst elementu menu są określane przez strukturę MYITEM danego elementu menu. Aplikacja wybiera kolory tekstu i tła odpowiednie dla stanu elementu menu.

Procedura okna przetwarza komunikat WM_DESTROY w celu zniszczenia czcionek i wolnej pamięci. Aplikacja usuwa czcionkę i zwalnia zdefiniowaną przez aplikację strukturę MYITEM dla każdego elementu menu.

Poniżej przedstawiono odpowiednie fragmenty pliku nagłówka aplikacji.

// 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 
 

Poniżej przedstawiono odpowiednie części procedury okna aplikacji i skojarzonych z nią funkcji.

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); 
} 

Używanie niestandardowych map bitowych znacznika wyboru

System udostępnia domyślną mapę bitową znacznika wyboru do wyświetlania obok wybranego elementu menu. Możesz dostosować pojedynczy element menu, podając parę map bitowych, aby zastąpić domyślną mapę bitową znacznika wyboru. System wyświetla jedną mapę bitową, gdy element jest zaznaczony, a drugi, gdy jest jasny. W tej sekcji opisano kroki dotyczące tworzenia i używania niestandardowych znaczników wyboru.

Tworzenie niestandardowych map bitowych znacznika wyboru

Niestandardowa mapa bitowa znacznika wyboru musi być taka sama jak domyślna mapa bitowa znacznika wyboru. Domyślny rozmiar znacznika wyboru mapy bitowej można pobrać, wywołując funkcję GetSystemMetrics. Słowo zwracane tej funkcji o niskiej kolejności określa szerokość; wyraz o wysokiej kolejności określa wysokość.

Możesz użyć zasobów mapy bitowej w celu dostarczenia map bitowych znacznika wyboru. Jednak ze względu na to, że wymagany rozmiar mapy bitowej różni się w zależności od typu wyświetlania, może być konieczne zmianę rozmiaru mapy bitowej w czasie wykonywania przy użyciu funkcji StretchBlt. W zależności od mapy bitowej zniekształcenie spowodowane ustalaniem rozmiaru może spowodować niedopuszczalne wyniki.

Zamiast używać zasobu mapy bitowej, możesz utworzyć mapę bitową w czasie wykonywania przy użyciu funkcji GDI.

Aby utworzyć mapę bitową w czasie wykonywania

  1. Użyj funkcji CreateCompatibleDC, aby utworzyć kontekst urządzenia zgodny z kontekstem używanym przez główne okno aplikacji.

    Parametr hdc funkcji może określać null lub wartość zwracaną z funkcji. CreateCompatibleDC zwraca uchwyt do kompatybilnego kontekstu urządzenia.

  2. Użyj funkcji CreateCompatibleBitmap, aby utworzyć mapę bitową zgodną z głównym oknem aplikacji.

    Parametry nWidth i nHeight ustawiają rozmiar mapy bitowej; powinny one określać informacje o szerokości i wysokości zwracane przez funkcję GetSystemMetrics.

    Notatka

    Możesz również użyć funkcji CreateBitmap, aby utworzyć monochromatyczną mapę bitową.

     

  3. Użyj funkcji SelectObject, aby wybrać mapę bitową w zgodnym kontekście urządzenia.

  4. Użyj funkcji rysowania GDI, takich jak Ellipse i LineTo, aby narysować obraz do mapy bitowej lub użyć takich funkcji jak BitBlt i StretchBlt, aby skopiować obraz do mapy bitowej.

Aby uzyskać więcej informacji, zobacz Mapy bitowe.

Kojarzenie map bitowych z elementem menu

Należy skojarzyć parę map bitowych znacznika wyboru z elementem menu, przekazując uchwyty map bitowych do funkcji SetMenuItemBitmaps. Parametr hBitmapUnchecked identyfikuje czystą mapę bitową, a hBitmapChecked parametr identyfikuje wybraną mapę bitową. Jeśli chcesz usunąć jeden lub oba znaczniki wyboru z elementu menu, możesz ustawić parametr hBitmapUnchecked lub hBitmapChecked, lub oba te elementy, jako NULL.

Ustawianie atrybutu znacznika wyboru

Funkcja CheckMenuItem ustawia atrybut znacznika wyboru elementu menu jako wybrany lub wyczyszczony. Możesz określić wartość MF_CHECKED, aby ustawić atrybut znacznika wyboru jako zaznaczony, a wartość MF_UNCHECKED, aby go wyczyścić.

Możesz również ustawić stan sprawdzania elementu menu przy użyciu funkcjiSetMenuItemInfo.

Czasami grupa elementów menu reprezentuje zestaw wzajemnie wykluczających się opcji. Korzystając z funkcji CheckMenuRadioItem, można sprawdzić jeden element menu, jednocześnie usuwając znacznik wyboru ze wszystkich innych elementów menu w grupie.

Symulowanie pól wyboru w menu

Ten temat zawiera przykład pokazujący sposób symulowania pól wyboru w menu. Przykład zawiera menu stylu czcionki, którego elementy służą do ustawienia pogrubienia, kursywy i podkreślenia dla bieżącej czcionki. Gdy atrybut czcionki jest w mocy, znacznik wyboru jest wyświetlany w polu wyboru obok odpowiedniego elementu menu; w przeciwnym razie obok elementu zostanie wyświetlone puste pole wyboru.

Przykład zastępuje domyślną mapę bitową znacznika wyboru dwiema mapami bitowymi: mapą bitową z zaznaczonym polem wyboru i mapą bitową z pustym polem. Zaznaczona mapa bitowa pola wyboru będzie wyświetlana obok elementu menu Pogrubienie, Kursywa lub Podkreślenie, gdy atrybut znaku wyboru tego elementu jest ustawiony na wartość MF_CHECKED. Mapa bitowa pustego lub wyczyszczonego pola wyboru jest wyświetlana, gdy atrybut pola wyboru jest ustawiony na MF_UNCHECKED.

System udostępnia wstępnie zdefiniowaną mapę bitową zawierającą obrazy używane do pól wyboru i przycisków radiowych. Przykład izoluje zaznaczone i puste pola wyboru, kopiuje je do dwóch oddzielnych map bitowych, a następnie używa ich jako map bitowych zaznaczonych i wyczyszczonych dla elementów w menu Znak.

Aby pobrać uchwyt do mapy bitowej zdefiniowanej przez system, przykład wywołuje funkcję LoadBitmap, określając NULL jako parametr hInstance i OBM_CHECKBOXES jako parametr lpBitmapName. Ponieważ obrazy w mapie bitowej mają taki sam rozmiar, przykład może je odizolować, dzieląc szerokość i wysokość mapy bitowej przez liczbę obrazów w wierszach i kolumnach.

W poniższej części pliku definicji zasobów pokazano, jak zdefiniowano elementy menu w menu Znak. Należy pamiętać, że początkowo nie obowiązują żadne atrybuty czcionki, więc atrybut znacznika wyboru dla elementu Regular jest ustawiony na zaznaczony, a domyślnie atrybut znacznika wyboru pozostałych elementów jest ustawiony na wyczyszczenie.

#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

Oto istotna zawartość pliku nagłówkowego aplikacji.

// 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); 

W poniższym przykładzie przedstawiono fragmenty procedury okna, które tworzą mapy bitowe znacznika wyboru; ustawiają atrybut znacznika wyboru elementów menu pogrubienia, kursywyi podkreślenia; oraz zniszcz mapy bitowe znacznika wyboru.

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; 
    } 
} 

Przykład użycia niestandardowych map bitowych znacznika wyboru

Przykład w tym temacie przypisuje niestandardowe bitmapy znaczników wyboru do elementów w dwóch różnych menu. Elementy menu z pierwszego menu określają atrybuty znaków: pogrubienie, kursywę i podkreślenie. Każdy element menu można wybrać lub wyczyścić. W przypadku tych elementów menu w przykładzie użyto map bitowych znacznika wyboru przypominających zaznaczone i wyczyszczone stany kontrolki pola wyboru.

Elementy menu w drugim menu określają ustawienia wyrównania akapitu: lewe, wyśrodkowane i prawe. W dowolnym momencie wybrano tylko jeden z tych elementów menu. W przypadku tych elementów menu w przykładzie użyto map bitowych znaków zaznaczenia przypominających stany zaznaczenia i wyczyszczenia kontroli przycisku radiowego.

Procedura okna przetwarza komunikat WM_CREATE przez wywołanie funkcji OnCreate zdefiniowanej przez aplikację. OnCreate tworzy cztery mapy bitowe znacznika wyboru, a następnie przypisuje je do odpowiednich elementów menu przy użyciu funkcji SetMenuItemBitmaps.

Aby utworzyć każdą mapę bitową, funkcja OnCreate wywołuje funkcję CreateMenuBitmaps zdefiniowaną przez aplikację, określając wskaźnik do funkcji rysunku specyficznego dla mapy bitowej. Funkcja CreateMenuBitmaps tworzy monochromatyczną mapę bitową o wymaganym rozmiarze, wybiera ją w kontekście urządzenia pamięci i usuwa tło. Następnie wywołuje określoną funkcję rysunku, aby wypełnić pierwszy plan.

Cztery funkcje rysunku zdefiniowane przez aplikację to DrawCheck, DrawUncheck, DrawRadioChecki DrawRadioUncheck. Rysują prostokąt z krzyżem, pusty prostokąt, elipsę zawierającą mniejszą wypełnioną elipsę oraz pustą elipsę.

Procedura okna przetwarza komunikat WM_DESTROY przez usunięcie map bitowych znacznika wyboru. Pobiera każdy uchwyt mapy bitowej przy użyciu funkcji GetMenuItemInfo, a następnie przekazuje uchwyt do funkcji.

Gdy użytkownik wybierze element menu, do okna właściciela zostanie wysłany komunikat WM_COMMAND. W przypadku elementów menu w menu Znak procedura okna wywołuje funkcję CheckCharacterItem zdefiniowaną przez aplikację. W przypadku elementów w menu akapitu procedura okna wywołuje funkcję CheckParagraphItem zdefiniowaną przez aplikację.

Każdy element w menu znaków można wybrać i wyczyścić niezależnie. W związku z tym checkCharacterItem po prostu przełącza stan sprawdzania określonego elementu menu. Najpierw funkcja wywołuje funkcję GetMenuItemInfo, aby uzyskać bieżący stan elementu menu. Następnie przełącza flagę stanu MFS_CHECKED i ustawia nowy stan przez wywołanie funkcji SetMenuItemInfo.

W przeciwieństwie do atrybutów znaków w danym momencie można wybrać tylko jedno wyrównanie akapitu. W związku z tym CheckParagraphItem sprawdza określony element menu i usuwa znacznik wyboru ze wszystkich innych elementów w menu. W tym celu wywołuje funkcję CheckMenuRadioItem .

Poniżej przedstawiono odpowiednie fragmenty pliku nagłówka aplikacji.

// 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); 
 

Poniżej przedstawiono odpowiednie części procedury okna aplikacji i powiązanych funkcji.

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); 
}