Delen via


Menu's gebruiken

In deze sectie worden de volgende taken beschreven:

Een Menu-Template-resource gebruiken

Meestal neemt u een menu op in een toepassing door een menusjabloonresource te maken en vervolgens het menu tijdens runtime te laden. In deze sectie wordt de indeling van een menusjabloon beschreven en wordt uitgelegd hoe u een menusjabloonresource laadt en gebruikt in uw toepassing. Zie de documentatie die is opgenomen in uw ontwikkelhulpprogramma's voor informatie over het maken van een menusjabloonresource.

Uitgebreide Menu-Template-indeling

De uitgebreide indeling van de menusjabloon ondersteunt extra menufunctionaliteit. Net als standaardmenusjabloonresources hebben uitgebreide menusjabloonresources het RT_MENU resourcetype. Het systeem onderscheidt de twee resourceindelingen op basis van het versienummer. Dit is het eerste lid van de resourceheader.

Een uitgebreide menusjabloon bestaat uit een MENUEX_TEMPLATE_HEADER structuur, gevolgd door nog een MENUEX_TEMPLATE_ITEM itemdefinitiestructuren.

Oude Menu-Template-indeling

Een oude menusjabloon (Microsoft Windows NT 3.51 en eerder) definieert een menu, maar biedt geen ondersteuning voor de nieuwe menufunctionaliteit. Een oude menusjabloonresource heeft het RT_MENU resourcetype.

Een oude menusjabloon bestaat uit een MENUITEMTEMPLATEHEADER structuur gevolgd door een of meer MENUITEMTEMPLATE structuren.

Een Menu-Template-resource laden

Als u een menusjabloonresource wilt laden, gebruikt u de functie LoadMenu, waarbij u een ingang opgeeft aan de module die de resource en de id van de menusjabloon bevat. De functie LoadMenu retourneert een menugreep die u kunt gebruiken om het menu toe te wijzen aan een venster. Dit venster wordt het venster eigenaar van het menu en ontvangt alle berichten die door het menu worden gegenereerd.

Gebruik de functie LoadMenuIndirect om een menu te maken op basis van een menusjabloon die al in het geheugen aanwezig is. Dit is handig als uw toepassing dynamisch menusjablonen genereert.

Als u een menu aan een venster wilt toewijzen, gebruikt u de functie SetMenu of geeft u de greep van het menu op in de parameter hMenu van de functie CreateWindowEx bij het maken van een venster. Een andere manier waarop u een menu aan een venster kunt toewijzen, is door een menusjabloon op te geven wanneer u een vensterklasse registreert; de sjabloon identificeert het opgegeven menu als het klasmenu voor die vensterklasse.

Als u wilt dat het systeem automatisch een specifiek menu aan een venster toewijst, geeft u de sjabloon van het menu op wanneer u de klasse van het venster registreert. De sjabloon identificeert het opgegeven menu als het klasmenu voor die vensterklasse. Wanneer u vervolgens een venster van de opgegeven klasse maakt, wijst het systeem het opgegeven menu automatisch toe aan het venster.

U kunt geen menu toewijzen aan een venster dat een onderliggend venster is.

Als u een klassemenu wilt maken, neemt u de id van de menusjabloonresource op als de lpszMenuName lid van een WNDCLASS- structuur en geeft u vervolgens een aanwijzer door aan de structuur aan de RegisterClass-functie.

Een klasmenu maken

In het volgende voorbeeld ziet u hoe u een klassenaam menu voor een applicatie maakt, een venster maakt dat gebruikmaakt van het klassenaam menu, en de menuopdrachten verwerkt in de vensterprocedure.

Hier volgt het relevante gedeelte van het headerbestand van de toepassing:

// Menu-template resource identifier 
 
#define IDM_MYMENURESOURCE   3

Hieronder volgen de relevante gedeelten van de toepassing zelf:

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

Een snelmenu maken

Als u een snelmenu in een toepassing wilt gebruiken, geeft u de greep door aan de functie TrackPopupMenuEx. Een toepassing roept doorgaans TrackPopupMenuEx- aan in een vensterprocedure als reactie op een door de gebruiker gegenereerd bericht, zoals WM_LBUTTONDOWN of WM_KEYDOWN.

Naast de pop-upmenuhandle vereist TrackPopupMenuEx dat u een handle aan het eigenaarsvenster specificeert, de positie van het contextmenu (in schermcoördinaten) en de muisknop die de gebruiker gebruikt om een item te kiezen.

De oudere TrackPopupMenu functie wordt nog steeds ondersteund, maar nieuwe toepassingen moeten de functie TrackPopupMenuEx gebruiken. De functie TrackPopupMenuEx vereist dezelfde parameters als TrackPopupMenu, maar u kunt ook een gedeelte van het scherm opgeven dat het menu niet moet verbergen. Een toepassing roept deze functies doorgaans aan in een vensterprocedure bij het verwerken van het WM_CONTEXTMENU bericht.

U kunt de positie van een snelmenu opgeven door x- en y-coördinaten op te geven, samen met de TPM_CENTERALIGN, TPM_LEFTALIGNof TPM_RIGHTALIGN vlag. De vlag geeft de positie van het snelmenu op ten opzichte van de x- en y-coördinaten.

U moet toestaan dat de gebruiker een item in een snelmenu kiest met behulp van dezelfde muisknop die wordt gebruikt om het menu weer te geven. Geef hiervoor TPM_LEFTBUTTON of TPM_RIGHTBUTTON vlag op om aan te geven welke muisknop de gebruiker kan gebruiken om een menu-item te kiezen.

Het WM_CONTEXTMENU-bericht verwerken

Het WM_CONTEXTMENU bericht wordt gegenereerd wanneer de vensterprocedure van een toepassing het WM_RBUTTONUP of WM_NCRBUTTONUP bericht doorgeeft aan de de functie DefWindowProc. De toepassing kan dit bericht verwerken om een snelmenu weer te geven dat geschikt is voor een specifiek gedeelte van het scherm. Als de toepassing geen snelmenu weergeeft, moet het bericht worden doorgegeven aan DefWindowProc- voor standaardafhandeling.

Hieronder volgt een voorbeeld van WM_CONTEXTMENU berichtverwerking, zoals deze kan worden weergegeven in de vensterprocedure van een toepassing. De woorden met lage volgorde en hoge volgorde van de lParam parameter geven de schermcoördinaten van de muis op wanneer de rechtermuisknop wordt vrijgegeven (houd er rekening mee dat deze coördinaten negatieve waarden kunnen aannemen op systemen met meerdere monitors). De toepassingsgedefinieerde OnContextMenu functie retourneert TRUE als er een contextmenu wordt weergegeven of ONWAAR als dat niet het geval is.

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

De volgende toepassingsgedefinieerde OnContextMenu-functie geeft een snelmenu weer als de opgegeven muispositie zich in het clientgebied van het venster bevindt. Een meer geavanceerde functie kan een van de verschillende menu’s weergeven, afhankelijk van welk deel van de clientomgeving wordt gespecificeerd. Als u het snelmenu daadwerkelijk wilt weergeven, roept dit voorbeeld een toepassingsgedefinieerde functie aan met de naam DisplayContextMenu. Zie Een snelmenu weergevenvoor een beschrijving van deze functie.

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

Een snelmenu Font-Attributes maken

Het voorbeeld in deze sectie bevat delen van code van een toepassing die een snelmenu maakt en weergeeft waarmee de gebruiker lettertypen en lettertypekenmerken kan instellen. De toepassing geeft het menu weer in het clientgebied van het hoofdvenster wanneer de gebruiker op de linkermuisknop klikt.

Hier volgt de menusjabloon voor het snelmenu dat is opgegeven in het resourcedefinitiebestand van de toepassing.

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 

In het volgende voorbeeld ziet u de vensterprocedure en ondersteunende functies die worden gebruikt om het snelmenu te maken en weer te geven.

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

Een snelmenu weergeven

De functie die in het volgende voorbeeld wordt weergegeven, geeft een snelmenu weer.

De toepassing bevat een menuresource die wordt geïdentificeerd door de tekenreeks 'ShortcutExample'. De menubalk bevat gewoon een menunaam. De toepassing gebruikt de functie TrackPopupMenu om het menu weer te geven dat is gekoppeld aan dit menu-item. (De menubalk zelf wordt niet weergegeven omdat TrackPopupMenu een greep naar een menu, submenu of snelmenu vereist.)

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

Het gebruik van Bitmaps Menu-Item

Het systeem kan een bitmap gebruiken in plaats van een tekenreeks om een menu-item weer te geven. Als u een bitmap wilt gebruiken, moet u de MIIM_BITMAP vlag instellen voor het menu-item en een ingang opgeven voor de bitmap die het systeem moet weergeven voor het menu-item in de hbmpItem lid van de MENUITEMINFO structuur. In deze sectie wordt beschreven hoe u bitmaps gebruikt voor menu-items.

De Bitmaptype-vlag instellen

De vlag MIIM_BITMAP of MF_BITMAP geeft aan dat het systeem een bitmap moet gebruiken in plaats van een tekenreeks om een menu-item weer te geven. De MIIM_BITMAP of MF_BITMAP vlag van een menu-item moet worden ingesteld tijdens de uitvoering; u kunt deze niet instellen in het resourcedefinitiebestand.

Voor nieuwe toepassingen kunt u de functie SetMenuItemInfo of InsertMenuItem gebruiken om het type vlag MIIM_BITMAP in te stellen. Als u een menu-item van een tekstitem wilt wijzigen in een bitmapitem, gebruikt u SetMenuItemInfo. Als u een nieuw bitmapitem aan een menu wilt toevoegen, gebruikt u de functie InsertMenuItem.

Toepassingen die zijn geschreven voor eerdere versies van het systeem, kunnen de functies ModifyMenu, InsertMenuof AppendMenu blijven gebruiken om de vlag MF_BITMAP in te stellen. Als u een menu-item van een tekenreeksitem wilt wijzigen in een bitmapitem, gebruikt u Menu Wijzigen. Als u een nieuw bitmapitem wilt toevoegen aan een menu, gebruikt u de vlag MF_BITMAP met de functie InsertMenu of AppendMenu.

De bitmap maken

Wanneer u de vlag voor het type MIIM_BITMAP of MF_BITMAP voor een menu-item instelt, moet u ook een ingang opgeven voor de bitmap die het systeem moet weergeven voor het menu-item. U kunt de bitmap opgeven als bitmapresource of de bitmap tijdens runtime maken. Als u een bitmapresource gebruikt, kunt u de functie LoadBitmap gebruiken om de bitmap te laden en het handvat ervan op te halen.

Als u de bitmap tijdens runtime wilt maken, gebruikt u GDI-functies (Windows Graphics Device Interface). GDI biedt verschillende manieren om een bitmap tijdens runtime te maken, maar ontwikkelaars gebruiken doorgaans de volgende methode:

  1. Gebruik de functie CreateCompatibleDC om een apparaatcontext te maken die compatibel is met de apparaatcontext die wordt gebruikt door het hoofdvenster van de toepassing.
  2. Gebruik de functie CreateCompatibleBitmap om een bitmap te maken die compatibel is met het hoofdvenster van de toepassing of gebruik de functie CreateBitmap om een monochrome bitmap te maken.
  3. Gebruik de functie SelectObject om de bitmap te selecteren in de context van het compatibele apparaat.
  4. Gebruik GDI-tekenfuncties, zoals Ellips en LineTo, om een afbeelding in de bitmap te tekenen.

Zie Bitmapsvoor meer informatie.

Lijnen en grafieken toevoegen aan een menu

In het volgende codevoorbeeld ziet u hoe u een menu maakt dat bitmaps voor menu-items bevat. Er worden twee menu's gemaakt. De eerste is een Grafiekenmenu dat drie menu-item bitmaps bevat: een cirkeldiagram, een lijndiagram en een staafdiagram. In het voorbeeld ziet u hoe u deze bitmaps uit het resourcebestand van de toepassing laadt en vervolgens de functies CreatePopupMenu en AppendMenu gebruikt om de menu- en menu-items te maken.

Het tweede menu is een menu Lijnen. Het bevat bitmaps met de lijnstijlen die worden geleverd door de vooraf gedefinieerde pen in het systeem. De bitmaps in lijnstijl worden tijdens runtime gemaakt met behulp van GDI-functies.

Hier volgen de definities van de bitmapresources in het resourcedefinitiebestand van de toepassing.

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

Dit zijn de relevante gedeelten van het headerbestand van de toepassing.

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

In het volgende voorbeeld ziet u hoe menu's en menu-item bitmaps worden gemaakt in een toepassing.

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

Voorbeeld van Menu-Item bitmaps

In het voorbeeld in dit onderwerp worden twee menu's gemaakt, elk met verschillende bitmapmenu-items. Voor elk menu voegt de toepassing een bijbehorende menunaam toe aan de menubalk van het hoofdvenster.

Het eerste menu bevat menu-items met elk van de drie grafiektypen: cirkel, lijn en staaf. De bitmaps voor deze menu-items worden gedefinieerd als resources en worden geladen met behulp van de functie LoadBitmap. Gekoppeld aan dit menu is een menunaam 'Grafiek' op de menubalk.

Het tweede menu bevat menu-items met elk van de vijf regelstijlen die worden gebruikt met de functie CreatePen: PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOTen PS_DASHDOTDOT. De toepassing maakt de bitmaps voor deze menu-items tijdens runtime met behulp van GDI-tekenfuncties. Gekoppeld aan dit menu is een Regels naam van het menu op de menubalk.

Gedefinieerd in de vensterprocedure van de toepassing zijn twee statische arrays van bitmap-handles. Eén matrix bevat de grepen van de drie bitmaps die worden gebruikt voor het menu Grafiek. De andere bevat de verwijzingen van de vijf bitmaps die gebruikt worden voor het menu Regels. Bij het verwerken van het WM_CREATE bericht, laadt de vensterprocedure de diagrambitmaps, maakt de lijnbitmaps en voegt vervolgens de bijbehorende menu-items toe. Bij het verwerken van het WM_DESTROY bericht worden alle bitmaps verwijderd door de vensterprocedure.

Hieronder vindt u de relevante gedeelten van het headerbestand van de toepassing.

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

Hieronder volgen de relevante gedeelten van de vensterprocedure. De vensterprocedure voert de meeste initialisatie uit door de door de toepassing gedefinieerde LoadChartBitmaps, CreateLineBitmaps en AddBitmapMenu-functies aan te roepen, die verderop in dit onderwerp worden beschreven.

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

Met de door de toepassing gedefinieerde functie LoadChartBitmaps worden de bitmapbronnen voor het grafiekmenu geladen door de LoadBitmap functie als volgt aan te roepen.

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

Met de toepassingsgedefinieerde functie CreateLineBitmaps worden de bitmaps voor het menu Lijnen gemaakt met behulp van GDI-tekenfuncties. De functie maakt een geheugenapparaatcontext (DC) met dezelfde eigenschappen als de DC van het bureaubladvenster. Voor elke lijnstijl maakt de functie een bitmap, selecteert deze in het geheugen-basis DC en tekent daarin.

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

De toepassingsgedefinieerde functie AddBitmapMenu maakt een menu en voegt het opgegeven aantal bitmapmenu-items eraan toe. Vervolgens wordt een bijbehorende menunaam toegevoegd aan de menubalk van het opgegeven venster.

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

Menu-items voor Owner-Drawn maken

Als u volledige controle nodig hebt over het uiterlijk van een menu-item, kunt u een eigen vormgegeven menu-item in uw applicatie gebruiken. In deze sectie worden de stappen beschreven die betrokken zijn bij het maken en gebruiken van een menu-item dat door de eigenaar is getekend.

De Owner-Drawn vlag instellen

U kunt geen door de eigenaar getekend menu-item definiëren in het resourcedefinitiebestand van uw toepassing. In plaats daarvan moet u een nieuw menu-item maken of een bestaande item wijzigen met behulp van de MFT_OWNERDRAW menuvlag.

U kunt de functie InsertMenuItem of SetMenuItemInfo gebruiken om een eigenaar-getekend menu-item op te geven. Gebruik InsertMenuItem om een nieuw menu-item in te voegen op de opgegeven positie in een menubalk of menu. Gebruik SetMenuItemInfo om de inhoud van een menu te wijzigen.

Wanneer u deze twee functies aanroept, moet u een aanwijzer opgeven naar een MENUITEMINFO structuur, waarmee de eigenschappen van het nieuwe menu-item of de eigenschappen die u wilt wijzigen voor een bestaand menu-item worden opgegeven. Als u een item een door de eigenaar getekend item wilt maken, geeft u de MIIM_FTYPE waarde op voor het fMask lid en de MFT_OWNERDRAW waarde voor het fType lid.

Door de juiste leden van de MENUITEMINFO structuur in te stellen, kunt u een door de toepassing gedefinieerde waarde, die itemgegevenswordt genoemd, koppelen aan elk menu-item. Hiertoe geeft u de MIIM_DATA waarde op voor het fMask-lid lid en de door de toepassing gedefinieerde waarde voor het dwItemData lid.

U kunt itemgegevens gebruiken met elk type menu-item, maar dit is met name handig voor door de eigenaar getekende items. Stel dat een structuur informatie bevat die wordt gebruikt om een menu-item te tekenen. Een toepassing kan de itemgegevens voor een menu-item gebruiken om een aanwijzer op te slaan in de structuur. De itemgegevens worden met de WM_MEASUREITEM en WM_DRAWITEM berichten verzonden naar het venster eigenaar van het menu. Als u de itemgegevens voor een menu op elk gewenst moment wilt ophalen, gebruikt u de functie GetMenuItemInfo.

Toepassingen die zijn geschreven voor eerdere versies van het systeem, kunnen AppendMenu, InsertMenuof Modify Menu blijven aanroepen om de vlag MF_OWNERDRAW toe te wijzen aan een menu-item dat door de eigenaar is getekend.

Wanneer u een van deze drie functies aanroept, kunt u een waarde doorgeven als de parameter lpNewItem. Deze waarde kan alle informatie vertegenwoordigen die zinvol is voor uw toepassing en die beschikbaar is voor uw toepassing wanneer het item wordt weergegeven. De waarde kan bijvoorbeeld een aanwijzer naar een structuur bevatten; de structuur kan op zijn beurt een tekenreeks bevatten en een ingang voor het logische lettertype dat uw toepassing gebruikt om de tekenreeks te tekenen.

Owner-Drawn menu's en het WM_MEASUREITEM bericht

Voordat het systeem voor het eerst een door de eigenaar getekend menu-item weergeeft, wordt het WM_MEASUREITEM bericht verzonden naar de vensterprocedure van het venster dat eigenaar is van het menu van het item. Dit bericht bevat een aanwijzer naar een MEASUREITEMSTRUCT structuur die het item identificeert en de itemgegevens bevat die een toepassing eraan kan hebben toegewezen. De vensterprocedure moet de itemWidth en itemHeight leden van de structuur invullen voordat het terugkeert van het verwerken van het bericht. Het systeem gebruikt de informatie in deze leden bij het maken van de begrenzingsrechthoek waarin een toepassing het menu-item tekent. Ook wordt de informatie gebruikt om te detecteren wanneer de gebruiker het item kiest.

Owner-Drawn Menu's en het WM_DRAWITEM-bericht

Wanneer het item moet worden getekend (bijvoorbeeld wanneer het voor het eerst wordt weergegeven of wanneer de gebruiker het selecteert), verzendt het systeem het WM_DRAWITEM bericht naar de vensterprocedure van het menu eigenaarvenster. Dit bericht bevat een aanwijzer naar een DRAWITEMSTRUCT structuur, die informatie over het item bevat, inclusief de itemgegevens die een toepassing eraan kan hebben toegewezen. Bovendien bevat DRAWITEMSTRUCT- vlaggen die de status van het item aangeven (zoals of het grijs of geselecteerd is) evenals een begrenzingsrechthoek en een apparaatcontext die door de toepassing wordt gebruikt om het item te tekenen.

Een toepassing moet het volgende doen tijdens het verwerken van het WM_DRAWITEM bericht:

  • Bepaal het type tekening dat nodig is. Hiervoor controleert u het itemAction lid van de DRAWITEMSTRUCT structuur.
  • Teken het menu-item op de juiste manier met behulp van de begrenzingsrechthoek en apparaatcontext die is verkregen uit de DRAWITEMSTRUCT structuur. De toepassing mag alleen binnen de begrenzingsrechthoek tekenen. Vanwege prestatieredenen knipt het systeem niet de delen van de afbeelding die buiten de rechthoek worden getekend.
  • Alle GDI-objecten herstellen die zijn geselecteerd voor de apparaatcontext van het menu-item.

Als de gebruiker het menu-item selecteert, stelt het systeem het itemAction lid van de DRAWITEMSTRUCT structuur in op de ODA_SELECT-waarde en stelt de ODS_SELECTED waarde in het itemState lid. Dit is de aanwijzing van een toepassing om het menu-item opnieuw te tekenen om aan te geven dat het is geselecteerd.

Owner-Drawn menu's en het WM_MENUCHAR bericht

Andere menu's dan door de eigenaar getekende menu's kunnen een menumnemonic opgeven door een onderstrepingsteken in te voegen naast een teken in de menutekenreeks. Hierdoor kan de gebruiker het menu selecteren door op Alt te drukken en op het menu mnemonic-teken te drukken. In door de eigenaar getekende menu's kunt u op deze manier echter geen menu-nemonic opgeven. In plaats daarvan moet uw toepassing het WM_MENUCHAR bericht verwerken om door de eigenaar getekende menu's met menumnemonics te bieden.

Het WM_MENUCHAR bericht wordt verzonden wanneer de gebruiker een menumnemonic typt die niet overeenkomt met een van de vooraf gedefinieerde nemonics van het huidige menu. De waarde in wParam geeft het ASCII-teken op dat overeenkomt met de sleutel die de gebruiker heeft ingedrukt met de ALT-toets. Het woord met een lage volgorde van wParam geeft het type van het geselecteerde menu aan en kan een van de volgende waarden zijn:

  • MF_POPUP als het huidige menu een submenu is.
  • MF_SYSMENU als het menu het systeemmenu is.

Het hoogste orde woord van wParam bevat de menuhandle voor het huidige menu. Het venster met de door de eigenaar getekende menu's kan als volgt WM_MENUCHAR verwerken:

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

De twee in het woord met hoge volgorde van de retourwaarde informeert het systeem dat het woord met lage volgorde van de retourwaarde de op nul gebaseerde index bevat van het menu-item dat moet worden geselecteerd.

De volgende constanten komen overeen met de mogelijke retourwaarden uit het WM_MENUCHAR bericht.

Constante Waarde Betekenis
MNC_IGNORE 0 Het systeem moet het teken negeren dat de gebruiker heeft ingedrukt en een korte pieptoon op de systeemspreker maken.
MNC_CLOSE 1 Het systeem moet het actieve menu sluiten.
MNC_EXECUTE 2 Het systeem moet het item kiezen dat is opgegeven in het laag-orde woord van de terugkeerwaarde. Het eigenaarsvenster ontvangt een WM_COMMAND bericht.
MNC_SELECT 3 Het systeem moet het item selecteren dat is opgegeven in het laag-orde woord van de retourwaarde.

 

Lettertypen instellen voor Menu-Item tekenreeksen

Dit onderwerp bevat een voorbeeld van een toepassing die gebruikmaakt van door de eigenaar getekende menu-items in een menu. Het menu bevat items die de kenmerken van het huidige lettertype instellen en de items worden weergegeven met het juiste lettertypekenmerk.

Hier ziet u hoe het menu wordt gedefinieerd in het resourcedefinitiebestand. Houd er rekening mee dat de tekenreeksen voor de menu-items Regular, Bold, Cursief en Onderstrepen tijdens runtime worden toegewezen, zodat de tekenreeksen leeg zijn in het resourcedefinitiebestand.

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

De vensterprocedure van de toepassing verwerkt de berichten die betrokken zijn bij het gebruik van door de eigenaar getekende menu-items. De toepassing gebruikt het WM_CREATE bericht om het volgende te doen:

  • Stel de MF_OWNERDRAW vlag in voor de menu-items.
  • Stel de tekenreeksen voor de menu-items in.
  • Haal handvatten op van de lettertypen die worden gebruikt om de items te tekenen.
  • Haal de tekst- en achtergrondkleurwaarden op voor geselecteerde menu-items.

De tekenreeksen en tekengrepen worden opgeslagen in een matrix met door de toepassing gedefinieerde MYITEM-structuren. De toepassingsgedefinieerde GetAFont-functie maakt een lettertype dat overeenkomt met het opgegeven lettertypekenmerk en retourneert een ingang naar het lettertype. De handles worden verwijderd tijdens de verwerking van het WM_DESTROY bericht.

Tijdens de verwerking van het WM_MEASUREITEM bericht verkrijgt het voorbeeld de breedte en hoogte van een tekenreeks voor een menu-item en kopieert deze waarden naar de MEASUREITEMSTRUCT structuur. Het systeem gebruikt de waarden voor breedte en hoogte om de grootte van het menu te berekenen.

Tijdens de verwerking van het WM_DRAWITEM bericht wordt de tekenreeks van het menu-item getekend met ruimte naast de tekenreeks voor de vinkjesbitmap. Als de gebruiker het item selecteert, worden de geselecteerde tekst- en achtergrondkleuren gebruikt om het item te tekenen.

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

Voorbeeld van Owner-Drawn menu-items

In het voorbeeld in dit onderwerp worden door de eigenaar getekende menu-items in een menu gebruikt. De menu-items selecteren specifieke lettertypekenmerken en de toepassing geeft elke menu-item weer met behulp van een lettertype met het bijbehorende kenmerk. De menuopdracht Cursief wordt bijvoorbeeld weergegeven in een cursief lettertype. De menunaam met teken op de menubalk opent het menu.

De menubalk en de vervolgkeuzelijst worden in eerste instantie gedefinieerd door een uitgebreide resource voor menusjablonen. Omdat een menusjabloon geen items met eigen tekenstijl kan opgeven, bevat het menu in eerste instantie vier tekstmenu-items met de volgende tekenreeksen: 'Normaal', 'Vet', 'Cursief' en 'Onderstreept'. De vensterprocedure van de toepassing wijzigt deze in items met eigen tekenstijl wanneer het WM_CREATE bericht wordt verwerkt. Wanneer het bericht WM_CREATE wordt ontvangen, roept de vensterprocedure de toepassingsgedefinieerde OnCreate-functie aan, waarmee de volgende stappen worden uitgevoerd voor elk menu-item:

  • Wijst een door de toepassing gedefinieerde MYITEM-structuur toe.
  • Haalt de tekst van het menu-item op en slaat deze op in de door de toepassing gedefinieerde MYITEM-structuur.
  • Hiermee maakt u het lettertype dat wordt gebruikt om het menu-item weer te geven en slaat u de greep op in de door de toepassing gedefinieerde MYITEM-structuur.
  • Hiermee wijzigt u het menu-itemtype in MFT_OWNERDRAW en slaat u een aanwijzer op in de door de toepassing gedefinieerde MYITEM-structuur als itemgegevens.

Omdat een aanwijzer naar elke door de toepassing gedefinieerde MYITEM-structuur wordt opgeslagen als itemgegevens, wordt deze doorgegeven aan de vensterprocedure in combinatie met de WM_MEASUREITEM en WM_DRAWITEM berichten voor het bijbehorende menu-item. De aanwijzer zit in het itemData--veld binnen zowel de MEASUREITEMSTRUCT als de DRAWITEMSTRUCT structuren.

Er wordt een WM_MEASUREITEM bericht verzonden voor elke door de eigenaar getekend menu-item wanneer het voor het eerst wordt weergegeven. De toepassing verwerkt dit bericht door het lettertype voor het menu-item in een apparaatcontext te selecteren en vervolgens de ruimte te bepalen die nodig is om de tekst van het menu-item in dat lettertype weer te geven. Het lettertype en de tekst van het menu-item worden beide opgegeven door de MYITEM structuur van het menu-item (de structuur die door de toepassing is gedefinieerd). De toepassing bepaalt de grootte van de tekst met behulp van de functie GetTextExtentPoint32.

De vensterprocedure verwerkt het WM_DRAWITEM bericht door de tekst van het menu-item in het juiste lettertype weer te geven. Het lettertype en de tekst van het menu-item worden beide opgegeven door de MYITEM structuur van het menu-item. De toepassing selecteert tekst- en achtergrondkleuren die geschikt zijn voor de status van het menu-item.

De vensterprocedure verwerkt het WM_DESTROY bericht om lettertypen en geheugen te vernietigen. In de toepassing wordt het lettertype verwijderd en wordt de door de toepassing gedefinieerde MYITEM-structuur voor elk menu-item vrijgemaakt.

Hier volgen de relevante gedeelten van het headerbestand van de toepassing.

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

Hier volgen de relevante gedeelten van de vensterprocedure van de toepassing en de bijbehorende functies.

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

Aangepaste bitmaps voor controlemarkeringen gebruiken

Het systeem biedt een standaard bitmap met vinkje voor weergave naast een menu-item dat is geselecteerd. U kunt een afzonderlijk menu-item aanpassen door een paar bitmaps op te geven om de standaard vinkje-afbeelding te vervangen. In het systeem wordt één bitmap weergegeven wanneer het item is geselecteerd en de andere wanneer het wordt gewist. In deze sectie worden de stappen beschreven voor het maken en gebruiken van aangepaste bitmaps voor vinkjes.

Aangepaste bitmaps voor vinkjes maken

Een aangepaste bitmap met vinkje moet dezelfde grootte hebben als de standaard bitmap voor vinkje. U kunt de standaardgrootte van het vinkje van de bitmap ophalen door de functie GetSystemMetrics aan te roepen. Het woord met lage volgorde van de retourwaarde van deze functie geeft de breedte aan; het woord met hoge volgorde geeft de hoogte aan.

U kunt bitmapbronnen gebruiken om bitmaps met vinkjes te voorzien. Omdat de vereiste bitmapgrootte echter afhankelijk is van het weergavetype, moet u mogelijk het formaat van de bitmap tijdens runtime wijzigen met behulp van de functie StretchBlt. Afhankelijk van de bitmap kan de vervorming die wordt veroorzaakt door de grootte, onacceptabele resultaten opleveren.

In plaats van een bitmapresource te gebruiken, kunt u tijdens runtime een bitmap maken met behulp van GDI-functies.

Een bitmap maken tijdens runtime

  1. Gebruik de functie CreateCompatibleDC om een apparaatcontext te maken die compatibel is met de context die door het hoofdvenster van de toepassing wordt gebruikt.

    De parameter hdc van de functie kan NULL- of de retourwaarde van de functie opgeven. CreateCompatibleDC retourneert een handle naar de compatibele apparaatcontext.

  2. Gebruik de functie CreateCompatibleBitmap om een bitmap te maken die compatibel is met het hoofdvenster van de toepassing.

    De nWidth en nHeight parameters stellen de grootte van de bitmap in; ze moeten de breedte- en hoogtegegevens opgeven die worden geretourneerd door de functie GetSystemMetrics.

    Notitie

    U kunt ook de functie CreateBitmap gebruiken om een monochrome bitmap te maken.

     

  3. Gebruik de functie SelectObject om de bitmap te selecteren in de context van het compatibele apparaat.

  4. Gebruik GDI-tekenfuncties, zoals Ellipse en LineTo, om een afbeelding in de bitmap te tekenen of gebruik functies zoals BitBlt- en StretchBlt- om een afbeelding naar de bitmap te kopiëren.

Zie Bitmapsvoor meer informatie.

Bitmaps koppelen aan een menu-item

U koppelt een paar bitmaps met vinkjes aan een menu-item door de grepen van de bitmaps door te geven aan de SetMenuItemBitmaps functie. De parameter hBitmapUnchecked identificeert de duidelijke bitmap en de parameter hBitmapChecked identificeert de geselecteerde bitmap. Als u een of beide vinkjes uit een menu-item wilt verwijderen, kunt u de hBitmapUnchecked of hBitmapChecked parameter of beide instellen op NULL-.

Het kenmerk Vinkje instellen

Met de functie CheckMenuItem wordt het vinkje van een menu-item ingesteld op geselecteerd of uitgeschakeld. U kunt de MF_CHECKED waarde opgeven om de vinkje-eigenschap in te stellen op geselecteerd en de MF_UNCHECKED waarde om deze in te stellen op ongedaan maken.

U kunt ook de controlestatus van een menu-item instellen met behulp van de functie SetMenuItemInfo.

Soms vertegenwoordigt een groep menu-items een reeks wederzijds exclusieve opties. Met behulp van de functie CheckMenuRadioItem kunt u één menu-item controleren en tegelijkertijd het vinkje uit alle andere menu-items in de groep verwijderen.

Selectievakjes simuleren in een menu

Dit onderwerp bevat een voorbeeld waarin wordt getoond hoe u selectievakjes in een menu simuleert. Het voorbeeld bevat een Tekenmenu waarvan de items de gebruiker in staat stellen om de vetgedrukte, cursieve en onderstreepte attributen van het huidige lettertype in te stellen. Wanneer een lettertypekenmerk van kracht is, wordt er een vinkje weergegeven in het selectievakje naast het bijbehorende menu-item; anders wordt er een leeg selectievakje naast het item weergegeven.

In het voorbeeld wordt de standaard vink bitmap vervangen door twee bitmaps: een bitmap met een geselecteerd selectievakje en een bitmap met een leeg selectievakje. De geselecteerde bitmap van het selectievakje wordt weergegeven naast het menu-item Vet, Cursief of Onderstrepen wanneer het vinkje van het item is ingesteld op MF_CHECKED. De bitmap van het lege of onbevinkte selectievakje wordt getoond wanneer de vinkje-eigenschap is ingesteld op MF_UNCHECKED.

Het systeem biedt een vooraf gedefinieerde bitmap die afbeeldingen bevat die gebruikt worden voor selectievakjes en radioknoppen. Het voorbeeld isoleert de geselecteerde en lege selectievakjes, kopieert ze naar twee afzonderlijke bitmaps en gebruikt ze vervolgens als de geselecteerde en gewiste bitmaps voor items in het menu Teken.

Om een handle op te halen naar de door het systeem gedefinieerde selectievakbitmap, roept het voorbeeld de functie LoadBitmap aan, waarbij NULL wordt opgegeven als de hInstance-parameter en OBM_CHECKBOXES als de lpBitmapName-parameter. Omdat de afbeeldingen in de bitmap allemaal dezelfde grootte hebben, kan het voorbeeld deze isoleren door de bitmapbreedte en hoogte te delen door het aantal afbeeldingen in de rijen en kolommen ervan te verdelen.

In het volgende gedeelte van een resourcedefinitiebestand ziet u hoe de menu-items in het menu Teken zijn gedefinieerd. Houd er rekening mee dat in eerste instantie geen lettertypekenmerken van kracht zijn, dus het vinkje voor het Normaal item is ingesteld op geselecteerd en standaard is het vinkje van de resterende items ingesteld op wissen.

#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

Hier volgt de relevante inhoud van het headerbestand van de toepassing.

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

In het volgende voorbeeld ziet u de gedeelten van de vensterprocedure waarmee de bitmaps van het selectievinkje worden gemaakt; het selectievinkje van de Vet, Cursiefen Onderstrepen menu-items instellen; en de bitmaps van het selectievinkje vernietigen.

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

Voorbeeld van het gebruik van aangepaste aanvinktekens bitmaps

In het voorbeeld in dit onderwerp worden aangepaste bitmaps voor vinkjes toegewezen aan menu-items in twee menu's. In de menu-items in het eerste menu worden tekenkenmerken opgegeven: vet, cursief en onderstrepen. Elk menu-item kan worden geselecteerd of gewist. Voor deze menu-items gebruikt het voorbeeld bitmapafbeeldingen van vinkjes die lijken op de geselecteerde en niet-geselecteerde statussen van een selectievakje.

De menu-items in het tweede menu geven instellingen voor alinea-uitlijning op: links, gecentreerd en rechts. Slechts één van deze menu-items is op elk gewenst moment geselecteerd. Voor deze menu-items gebruikt het voorbeeld bitmaps met vinkjes die lijken op de geselecteerde en gewiste staten van een radioknop.

De vensterprocedure verwerkt het WM_CREATE bericht door de door de toepassing gedefinieerde functie OnCreate aan te roepen. OnCreate maakt de vier vinkjes bitmaps en wijst deze vervolgens toe aan de juiste menu-items met behulp van de SetMenuItemBitmaps functie.

Als u elke bitmap wilt maken, roept OnCreate de toepassingsgedefinieerde functie CreateMenuBitmaps aan, waarbij een aanwijzer naar een bitmapspecifieke tekenfunctie wordt opgegeven. Met CreateMenuBitmaps maakt u een monochrome bitmap van de vereiste grootte, selecteert hij in een geheugencontext en wist de achtergrond. Vervolgens wordt de opgegeven tekenfunctie aangeroepen om de voorgrond in te vullen.

De vier toepassingsgedefinieerde tekenfuncties zijn DrawCheck, DrawUncheck, DrawRadioChecken DrawRadioUncheck. Ze tekenen een rechthoek met een X, een lege rechthoek, een ellips met een kleinere gevulde ellips en een lege ellips.

De vensterprocedure verwerkt het WM_DESTROY bericht door de bitmaps van het vinkje te verwijderen. Hiermee wordt elk bitmaphandle opgehaald met behulp van de functie GetMenuItemInfo en vervolgens wordt een handle doorgegeven aan de functie.

Wanneer de gebruiker een menu-item kiest, wordt er een WM_COMMAND bericht verzonden naar het venster eigenaar. Voor menu-items in het menu Character roept de vensterprocedure de functie CheckCharacterItem aan. Voor items in het menu Alinea roept de vensterprocedure de door de toepassing gedefinieerde functie CheckParagraphItem aan.

Elk item in het menu Teken kan onafhankelijk worden geselecteerd en gewist. Daarom schakelt CheckCharacterItem simpelweg de controlestatus van het opgegeven menu-item in. Eerst roept de functie de GetMenuItemInfo- functie aan om de status van het huidige menu-item op te halen. Vervolgens wordt de MFS_CHECKED statusvlag gewijzigd en wordt de nieuwe status ingesteld door de functie SetMenuItemInfo aan te roepen.

In tegenstelling tot tekenkenmerken kan slechts één alinea-uitlijning tegelijk worden geselecteerd. Daarom vinkt CheckParagraphItem het opgegeven menu-item aan en verwijdert het het vinkje van alle andere items in het menu. Hiervoor wordt de functie CheckMenuRadioItem aangeroepen.

Hieronder vindt u de relevante gedeelten van het headerbestand van de toepassing.

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

Hier volgen de relevante gedeelten van de vensterprocedure van de toepassing en gerelateerde functies.

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