Utilisation des menus

Cette section décrit les tâches suivantes :

Utilisation d’une ressource Menu-Template

Vous incluez généralement un menu dans une application en créant une ressource de modèle de menu, puis en chargeant le menu au moment de l’exécution. Cette section décrit le format d’un modèle de menu et explique comment charger une ressource de modèle de menu et l’utiliser dans votre application. Pour plus d’informations sur la création d’une ressource de modèle de menu, consultez la documentation incluse avec vos outils de développement.

Format Menu-Template étendu

Le format de modèle de menu étendu prend en charge des fonctionnalités de menu supplémentaires. Comme les ressources de modèle de menu standard, les ressources de modèle de menu étendu ont le type de ressource RT_MENU . Le système distingue les deux formats de ressource par le numéro de version, qui est le premier membre de l’en-tête de ressource.

Un modèle de menu étendu se compose d’une structure MENUEX_TEMPLATE_HEADER suivie d’une autre MENUEX_TEMPLATE_ITEM structures de définition d’élément .

Ancien format Menu-Template

Un ancien modèle de menu (Microsoft Windows NT 3.51 et versions antérieures) définit un menu, mais ne prend pas en charge la nouvelle fonctionnalité de menu. Une ancienne ressource de modèle de menu a le type de ressource RT_MENU .

Un ancien modèle de menu se compose d’une structure MENUITEMTEMPLATEHEADER suivie d’une ou plusieurs structures MENUITEMTEMPLATE .

Chargement d’une ressource Menu-Template

Pour charger une ressource de modèle de menu, utilisez la fonction LoadMenu , en spécifiant un handle au module qui contient la ressource et l’identificateur du modèle de menu. La fonction LoadMenu retourne un handle de menu que vous pouvez utiliser pour affecter le menu à une fenêtre. Cette fenêtre devient la fenêtre propriétaire du menu, recevant tous les messages générés par le menu.

Pour créer un menu à partir d’un modèle de menu déjà en mémoire, utilisez la fonction LoadMenuIndirect . Cela est utile si votre application génère dynamiquement des modèles de menu.

Pour affecter un menu à une fenêtre, utilisez la fonction SetMenu ou spécifiez le handle du menu dans le paramètre hMenu de la fonction CreateWindowEx lors de la création d’une fenêtre. Une autre façon d’affecter un menu à une fenêtre consiste à spécifier un modèle de menu lorsque vous inscrivez une classe de fenêtre ; le modèle identifie le menu spécifié en tant que menu de classe pour cette classe de fenêtre.

Pour que le système attribue automatiquement un menu spécifique à une fenêtre, spécifiez le modèle de menu lorsque vous inscrivez la classe de la fenêtre. Le modèle identifie le menu spécifié comme menu de classe pour cette classe de fenêtre. Ensuite, lorsque vous créez une fenêtre de la classe spécifiée, le système affecte automatiquement le menu spécifié à la fenêtre.

Vous ne pouvez pas affecter un menu à une fenêtre qui est une fenêtre enfant.

Pour créer un menu de classe, incluez l’identificateur de la ressource de modèle de menu en tant que membre lpszMenuName d’une structure WNDCLASS , puis passez un pointeur vers la structure vers la fonction RegisterClass .

Création d’un menu classe

L’exemple suivant montre comment créer un menu de classe pour une application, créer une fenêtre qui utilise le menu de classe et traiter les commandes de menu dans la procédure de fenêtre.

Voici la partie pertinente du fichier d’en-tête de l’application :

// Menu-template resource identifier 
 
#define IDM_MYMENURESOURCE   3

Voici les parties pertinentes de l’application elle-même :

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

Création d’un menu contextuel

Pour utiliser un menu contextuel dans une application, passez son handle à la fonction TrackPopupMenuEx . Une application appelle généralement TrackPopupMenuEx dans une procédure de fenêtre en réponse à un message généré par l’utilisateur, tel que WM_LBUTTONDOWN ou WM_KEYDOWN.

Outre le handle de menu contextuel, TrackPopupMenuEx vous oblige à spécifier un handle pour la fenêtre propriétaire, la position du menu contextuel (en coordonnées d’écran) et le bouton de la souris que l’utilisateur peut utiliser pour choisir un élément.

L’ancienne fonction TrackPopupMenu est toujours prise en charge, mais les nouvelles applications doivent utiliser la fonction TrackPopupMenuEx . La fonction TrackPopupMenuEx nécessite les mêmes paramètres que TrackPopupMenu, mais vous permet également de spécifier une partie de l’écran que le menu ne doit pas masquer. Une application appelle généralement ces fonctions dans une procédure de fenêtre lors du traitement du message WM_CONTEXTMENU .

Vous pouvez spécifier la position d’un menu contextuel en fournissant des coordonnées x et y, ainsi que l’indicateur TPM_CENTERALIGN, TPM_LEFTALIGN ou TPM_RIGHTALIGN . L’indicateur spécifie la position du menu contextuel par rapport aux coordonnées x et y.

Vous devez autoriser l’utilisateur à choisir un élément dans un menu contextuel en utilisant le bouton de la souris utilisé pour afficher le menu. Pour ce faire, spécifiez TPM_LEFTBUTTON ou TPM_RIGHTBUTTON indicateur pour indiquer le bouton de la souris que l’utilisateur peut utiliser pour choisir un élément de menu.

Traitement du message WM_CONTEXTMENU

Le message WM_CONTEXTMENU est généré lorsque la procédure de fenêtre d’une application transmet le message WM_RBUTTONUP ou WM_NCRBUTTONUP à la fonction DefWindowProc . L’application peut traiter ce message pour afficher un menu contextuel approprié à une partie spécifique de son écran. Si l’application n’affiche pas de menu contextuel, elle doit passer le message à DefWindowProc pour la gestion par défaut.

Voici un exemple de traitement des messages WM_CONTEXTMENU tel qu’il peut apparaître dans la procédure de fenêtre d’une application. Les mots d’ordre inférieur et d’ordre élevé du paramètre lParam spécifient les coordonnées d’écran de la souris lorsque le bouton droit de la souris est relâché (notez que ces coordonnées peuvent prendre des valeurs négatives sur les systèmes avec plusieurs moniteurs). La fonction OnContextMenu définie par l’application retourne TRUE si elle affiche un menu contextuel, ou FALSE si ce n’est pas le cas.

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

La fonction OnContextMenu définie par l’application suivante affiche un menu contextuel si la position de la souris spécifiée se trouve dans la zone cliente de la fenêtre. Une fonction plus sophistiquée peut afficher l’un des différents menus, selon la partie de la zone cliente spécifiée. Pour afficher réellement le menu contextuel, cet exemple appelle une fonction définie par l’application appelée DisplayContextMenu. Pour obtenir une description de cette fonction, consultez Affichage d’un menu contextuel.

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

Création d’un menu de Font-Attributes raccourci

L’exemple de cette section contient des parties de code d’une application qui crée et affiche un menu contextuel qui permet à l’utilisateur de définir des polices et des attributs de police. L’application affiche le menu dans la zone cliente de sa fenêtre de main chaque fois que l’utilisateur clique sur le bouton gauche de la souris.

Voici le modèle de menu pour le menu contextuel fourni dans le fichier de définition de ressource de l’application.

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 

L’exemple suivant montre la procédure de fenêtre et les fonctions de prise en charge utilisées pour créer et afficher le menu contextuel.

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

Affichage d’un menu contextuel

La fonction illustrée dans l’exemple suivant affiche un menu contextuel.

L’application inclut une ressource de menu identifiée par la chaîne « ShortcutExample ». La barre de menus contient simplement un nom de menu. L’application utilise la fonction TrackPopupMenu pour afficher le menu associé à cet élément de menu. (La barre de menus elle-même ne s’affiche pas, car TrackPopupMenu nécessite un handle pour un menu, un sous-menu ou un menu contextuel.)

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

Utilisation de bitmaps Menu-Item

Le système peut utiliser une bitmap au lieu d’une chaîne de texte pour afficher un élément de menu. Pour utiliser une bitmap, vous devez définir l’indicateur MIIM_BITMAP pour l’élément de menu et spécifier un handle pour la bitmap que le système doit afficher pour l’élément de menu dans le membre hbmpItem de la structure MENUITEMINFO . Cette section explique comment utiliser des bitmaps pour les éléments de menu.

Définition de l’indicateur de type bitmap

L’indicateur MIIM_BITMAP ou MF_BITMAP indique au système d’utiliser une bitmap plutôt qu’une chaîne de texte pour afficher un élément de menu. L’indicateur MIIM_BITMAP ou MF_BITMAP d’un élément de menu doit être défini au moment de l’exécution . vous ne pouvez pas la définir dans le fichier de définition de ressource.

Pour les nouvelles applications, vous pouvez utiliser la fonction SetMenuItemInfo ou InsertMenuItem pour définir l’indicateur de type MIIM_BITMAP . Pour remplacer un élément de menu d’un élément de texte par un élément bitmap, utilisez SetMenuItemInfo. Pour ajouter un nouvel élément bitmap à un menu, utilisez la fonction InsertMenuItem .

Les applications écrites pour des versions antérieures du système peuvent continuer à utiliser les fonctions ModifyMenu, InsertMenu ou AppendMenu pour définir l’indicateur MF_BITMAP . Pour remplacer un élément de menu d’un élément de chaîne de texte par un élément bitmap, utilisez ModifyMenu. Pour ajouter un nouvel élément bitmap à un menu, utilisez l’indicateur MF_BITMAP avec la fonction InsertMenu ou AppendMenu .

Création de la bitmap

Lorsque vous définissez l’indicateur de type MIIM_BITMAP ou MF_BITMAP pour un élément de menu, vous devez également spécifier un handle pour la bitmap que le système doit afficher pour l’élément de menu. Vous pouvez fournir la bitmap en tant que ressource bitmap ou créer la bitmap au moment de l’exécution. Si vous utilisez une ressource bitmap, vous pouvez utiliser la fonction LoadBitmap pour charger la bitmap et obtenir son handle.

Pour créer la bitmap au moment de l’exécution, utilisez les fonctions GDI (Graphics Device Interface) Windows. GDI offre plusieurs façons de créer une bitmap au moment de l’exécution, mais les développeurs utilisent généralement la méthode suivante :

  1. Utilisez la fonction CreateCompatibleDC pour créer un contexte d’appareil compatible avec le contexte d’appareil utilisé par la fenêtre main de l’application.
  2. Utilisez la fonction CreateCompatibleBitmap pour créer une bitmap compatible avec la fenêtre main de l’application ou utilisez la fonction CreateBitmap pour créer une bitmap monochrome.
  3. Utilisez la fonction SelectObject pour sélectionner la bitmap dans le contexte de l’appareil compatible.
  4. Utilisez des fonctions de dessin GDI, telles que Ellipse et LineTo, pour dessiner une image dans la bitmap.

Pour plus d’informations, consultez Bitmaps.

Ajout de lignes et de graphiques à un menu

L’exemple de code suivant montre comment créer un menu qui contient des bitmaps d’éléments de menu. Il crée deux menus. Le premier est un menu Graphique qui contient trois bitmaps d’éléments de menu : un graphique en secteurs, un graphique en courbes et un graphique à barres. L’exemple montre comment charger ces bitmaps à partir du fichier de ressources de l’application, puis utiliser les fonctions CreatePopupMenu et AppendMenu pour créer les éléments de menu et de menu.

Le deuxième menu est un menu Lignes. Il contient des bitmaps montrant les styles de trait fournis par le stylet prédéfini dans le système. Les bitmaps de style ligne sont créées au moment de l’exécution à l’aide de fonctions GDI.

Voici les définitions des ressources bitmap dans le fichier de définition de ressource de l’application.

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

Voici les parties pertinentes du fichier d’en-tête de l’application.

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

L’exemple suivant montre comment les menus et les bitmaps d’éléments de menu sont créés dans une application.

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

Exemple de bitmaps Menu-Item

L’exemple de cette rubrique crée deux menus, chacun contenant plusieurs éléments de menu bitmap. Pour chaque menu, l’application ajoute un nom de menu correspondant à la barre de menus de la fenêtre main.

Le premier menu contient des éléments de menu montrant chacun des trois types de graphiques : secteurs, lignes et barres. Les bitmaps de ces éléments de menu sont définies en tant que ressources et sont chargées à l’aide de la fonction LoadBitmap . Ce menu est associé à un nom de menu « Graphique » dans la barre de menus.

Le deuxième menu contient des éléments de menu montrant chacun des cinq styles de ligne utilisés avec la fonction CreatePen : PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT et PS_DASHDOTDOT. L’application crée les bitmaps pour ces éléments de menu au moment de l’exécution à l’aide des fonctions de dessin GDI. Ce menu est associé à un nom de menu Lignes dans la barre de menus.

La procédure de fenêtre de l’application est définie par deux tableaux statiques de handles bitmap. Un tableau contient les handles des trois bitmaps utilisées pour le menu Graphique . L’autre contient les handles des cinq bitmaps utilisées pour le menu Lignes . Lors du traitement du message WM_CREATE , la procédure de fenêtre charge les bitmaps de graphique, crée les bitmaps de ligne, puis ajoute les éléments de menu correspondants. Lors du traitement du message WM_DESTROY , la procédure de fenêtre supprime toutes les bitmaps.

Voici les parties pertinentes du fichier d’en-tête de l’application.

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

Voici les parties pertinentes de la procédure de fenêtre. La procédure de fenêtre effectue la majeure partie de son initialisation en appelant les fonctions LoadChartBitmaps, CreateLineBitmaps et AddBitmapMenu définies par l’application, décrites plus loin dans cette rubrique.

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

La fonction LoadChartBitmaps définie par l’application charge les ressources bitmap du menu de graphique en appelant la fonction LoadBitmap , comme suit.

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

La fonction CreateLineBitmaps définie par l’application crée les bitmaps du menu Lignes à l’aide des fonctions de dessin GDI. La fonction crée un contexte de périphérique mémoire (DC) avec les mêmes propriétés que le contrôleur de domaine de la fenêtre de bureau. Pour chaque style de ligne, la fonction crée une bitmap, la sélectionne dans le contrôleur de domaine de la mémoire et y dessine.

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

La fonction AddBitmapMenu définie par l’application crée un menu et y ajoute le nombre spécifié d’éléments de menu bitmap. Ensuite, il ajoute un nom de menu correspondant à la barre de menus de la fenêtre spécifiée.

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

Création d’éléments de menu Owner-Drawn

Si vous avez besoin d’un contrôle total sur l’apparence d’un élément de menu, vous pouvez utiliser un élément de menu dessiné par le propriétaire dans votre application. Cette section décrit les étapes de création et d’utilisation d’un élément de menu dessiné par le propriétaire.

Définition de l’indicateur de Owner-Drawn

Vous ne pouvez pas définir un élément de menu dessiné par le propriétaire dans le fichier de définition de ressource de votre application. Au lieu de cela, vous devez créer un élément de menu ou en modifier un existant à l’aide de l’indicateur de menu MFT_OWNERDRAW .

Vous pouvez utiliser la fonction InsertMenuItem ou SetMenuItemInfo pour spécifier un élément de menu dessiné par le propriétaire. Utilisez InsertMenuItem pour insérer un nouvel élément de menu à la position spécifiée dans une barre de menus ou un menu. Utilisez SetMenuItemInfo pour modifier le contenu d’un menu.

Lorsque vous appelez ces deux fonctions, vous devez spécifier un pointeur vers une structure MENUITEMINFO , qui spécifie les propriétés du nouvel élément de menu ou les propriétés que vous souhaitez modifier pour un élément de menu existant. Pour faire d’un élément un élément dessiné par le propriétaire, spécifiez la valeur MIIM_FTYPE pour le membre fMask et la valeur MFT_OWNERDRAW pour le membre fType .

En définissant les membres appropriés de la structure MENUITEMINFO , vous pouvez associer une valeur définie par l’application, appelée données d’élément, à chaque élément de menu. Pour ce faire, spécifiez la valeur MIIM_DATA pour le membre fMask et la valeur définie par l’application pour le membre dwItemData .

Vous pouvez utiliser les données d’élément avec n’importe quel type d’élément de menu, mais elles sont particulièrement utiles pour les éléments dessinés par le propriétaire. Par exemple, supposons qu’une structure contient des informations utilisées pour dessiner un élément de menu. Une application peut utiliser les données d’élément pour un élément de menu afin de stocker un pointeur vers la structure. Les données d’élément sont envoyées à la fenêtre propriétaire du menu avec les messages WM_MEASUREITEM et WM_DRAWITEM . Pour récupérer les données d’élément d’un menu à tout moment, utilisez la fonction GetMenuItemInfo .

Les applications écrites pour les versions antérieures du système peuvent continuer à appeler AppendMenu, InsertMenu ou ModifyMenu pour affecter l’indicateur MF_OWNERDRAW à un élément de menu dessiné par le propriétaire.

Lorsque vous appelez l’une de ces trois fonctions, vous pouvez passer une valeur en tant que paramètre lpNewItem . Cette valeur peut représenter toutes les informations qui sont significatives pour votre application et qui seront disponibles pour votre application lorsque l’élément doit être affiché. Par exemple, la valeur peut contenir un pointeur vers une structure ; la structure, à son tour, peut contenir une chaîne de texte et un handle à la police logique que votre application utilisera pour dessiner la chaîne.

menus Owner-Drawn et message WM_MEASUREITEM

Avant que le système n’affiche un élément de menu dessiné par le propriétaire pour la première fois, il envoie le message WM_MEASUREITEM à la procédure de fenêtre de la fenêtre qui possède le menu de l’élément. Ce message contient un pointeur vers une structure MEASUREITEMSTRUCT qui identifie l’élément et contient les données d’élément qu’une application peut lui avoir attribuées. La procédure de fenêtre doit remplir les membres itemWidth et itemHeight de la structure avant de revenir du traitement du message. Le système utilise les informations contenues dans ces membres lors de la création du rectangle englobant dans lequel une application dessine l’élément de menu. Il utilise également les informations pour détecter quand l’utilisateur choisit l’élément.

menus Owner-Drawn et message WM_DRAWITEM

Chaque fois que l’élément doit être dessiné (par exemple, lorsqu’il est affiché pour la première fois ou lorsque l’utilisateur le sélectionne), le système envoie le message WM_DRAWITEM à la procédure de fenêtre de la fenêtre propriétaire du menu. Ce message contient un pointeur vers une structure DRAWITEMSTRUCT , qui contient des informations sur l’élément, y compris les données d’élément qu’une application peut lui avoir attribuées. En outre, DRAWITEMSTRUCT contient des indicateurs qui indiquent l’état de l’élément (par exemple, s’il est grisé ou sélectionné) ainsi qu’un rectangle englobant et un contexte d’appareil que l’application utilise pour dessiner l’élément.

Une application doit effectuer les opérations suivantes lors du traitement du message WM_DRAWITEM :

  • Déterminez le type de dessin nécessaire. Pour ce faire, case activée le membre itemAction de la structure DRAWITEMSTRUCT.
  • Dessinez correctement l’élément de menu à l’aide du rectangle englobant et du contexte d’appareil obtenus à partir de la structure DRAWITEMSTRUCT . L’application doit dessiner uniquement dans le rectangle englobant. Pour des raisons de performances, le système ne coupe pas les parties de l’image qui sont dessinées en dehors du rectangle.
  • Restaurez tous les objets GDI sélectionnés pour le contexte de l’appareil de l’élément de menu.

Si l’utilisateur sélectionne l’élément de menu, le système définit le membre itemAction de la structure DRAWITEMSTRUCT sur la valeur ODA_SELECT et définit la valeur ODS_SELECTED dans le membre itemState . Il s’agit d’un indicateur d’application permettant de redessiner l’élément de menu pour indiquer qu’il est sélectionné.

menus Owner-Drawn et message WM_MENUCHAR

Les menus autres que les menus dessinés par le propriétaire peuvent spécifier un menu mnémonique en insérant un trait de soulignement en regard d’un caractère dans la chaîne de menu. Cela permet à l’utilisateur de sélectionner le menu en appuyant sur ALT et en appuyant sur le caractère mnémonique du menu. Toutefois, dans les menus dessinés par le propriétaire, vous ne pouvez pas spécifier un menu mnémonique de cette manière. Au lieu de cela, votre application doit traiter le message WM_MENUCHAR pour fournir des menus dessinés par le propriétaire avec des mnémoniques de menu.

Le message WM_MENUCHAR est envoyé lorsque l’utilisateur tape un menu mnémonique qui ne correspond à aucune des mnémoniques prédéfinies du menu actif. La valeur contenue dans wParam spécifie le caractère ASCII qui correspond à la touche que l’utilisateur a enfoncée avec la touche ALT. Le mot de bas ordre de wParam spécifie le type du menu sélectionné et peut être l’une des valeurs suivantes :

  • MF_POPUP si le menu actif est un sous-menu.
  • MF_SYSMENU si le menu est le menu système.

Le mot d’ordre élevé de wParam contient le handle de menu du menu actif. La fenêtre avec les menus dessinés par le propriétaire peut traiter WM_MENUCHAR comme suit :

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

Les deux dans le mot d’ordre élevé de la valeur de retour informent le système que le mot de bas ordre de la valeur de retour contient l’index de base zéro de l’élément de menu à sélectionner.

Les constantes suivantes correspondent aux valeurs de retour possibles du message WM_MENUCHAR .

Constante Valeur Signification
MNC_IGNORE 0 Le système doit ignorer le caractère appuyé par l’utilisateur et créer un bref bip sur le haut-parleur système.
MNC_CLOSE 1 Le système doit fermer le menu actif.
MNC_EXECUTE 2 Le système doit choisir l’élément spécifié dans le mot de bas ordre de la valeur de retour. La fenêtre propriétaire reçoit un message WM_COMMAND .
MNC_SELECT 3 Le système doit sélectionner l’élément spécifié dans le mot de bas ordre de la valeur de retour.

 

Définition des polices pour les chaînes de texte Menu-Item

Cette rubrique contient un exemple d’application qui utilise des éléments de menu dessinés par le propriétaire dans un menu. Le menu contient des éléments qui définissent les attributs de la police actuelle, et les éléments sont affichés à l’aide de l’attribut de police approprié.

Voici comment le menu est défini dans le fichier de définition de ressource. Notez que les chaînes des éléments de menu Regular, Bold, Italic et Soulignement sont affectées au moment de l’exécution, de sorte que leurs chaînes sont vides dans le fichier de définition de ressource.

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

La procédure de fenêtre de l’application traite les messages impliqués dans l’utilisation d’éléments de menu dessinés par le propriétaire. L’application utilise le message WM_CREATE pour effectuer les opérations suivantes :

  • Définissez l’indicateur MF_OWNERDRAW pour les éléments de menu.
  • Définissez les chaînes de texte pour les éléments de menu.
  • Obtenez les handles des polices utilisées pour dessiner les éléments.
  • Obtenez les valeurs de texte et de couleur d’arrière-plan pour les éléments de menu sélectionnés.

Les chaînes de texte et les handles de police sont stockés dans un tableau de structures MYITEM définies par l’application. La fonction GetAFont définie par l’application crée une police qui correspond à l’attribut de police spécifié et retourne un handle à la police. Les handles sont détruits pendant le traitement du message WM_DESTROY .

Pendant le traitement du message WM_MEASUREITEM , l’exemple obtient la largeur et la hauteur d’une chaîne d’élément de menu et copie ces valeurs dans la structure MEASUREITEMSTRUCT . Le système utilise les valeurs de largeur et de hauteur pour calculer la taille du menu.

Pendant le traitement du message WM_DRAWITEM, la chaîne de l’élément de menu est dessinée avec de la place à gauche à côté de la chaîne de l’image bitmap de marque case activée. Si l’utilisateur sélectionne l’élément, le texte et les couleurs d’arrière-plan sélectionnés sont utilisées pour dessiner l’élément.

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

Exemple d’éléments de menu Owner-Drawn

L’exemple de cette rubrique utilise des éléments de menu dessinés par le propriétaire dans un menu. Les éléments de menu sélectionnent des attributs de police spécifiques, et l’application affiche chaque élément de menu à l’aide d’une police qui a l’attribut correspondant. Par exemple, l’élément de menu Italique s’affiche dans une police italique. Le nom du menu Caractère de la barre de menus ouvre le menu.

La barre de menus et le menu déroulant sont définis initialement par une ressource de modèle de menu étendu. Étant donné qu’un modèle de menu ne peut pas spécifier des éléments dessinés par le propriétaire, le menu contient initialement quatre éléments de menu de texte avec les chaînes suivantes : « Normal », « Gras », « Italique » et « Soulignement ». La procédure de fenêtre de l’application les transforme en éléments dessinés par le propriétaire lorsqu’elle traite le message WM_CREATE . Lorsqu’elle reçoit le message WM_CREATE , la procédure de fenêtre appelle la fonction OnCreate définie par l’application, qui effectue les étapes suivantes pour chaque élément de menu :

  • Alloue une structure MYITEM définie par l’application.
  • Obtient le texte de l’élément de menu et l’enregistre dans la structure MYITEM définie par l’application.
  • Crée la police utilisée pour afficher l’élément de menu et enregistre son handle dans la structure MYITEM définie par l’application.
  • Modifie le type d’élément de menu en MFT_OWNERDRAW et enregistre un pointeur vers la structure MYITEM définie par l’application en tant que données d’élément.

Étant donné qu’un pointeur vers chaque structure MYITEM définie par l’application est enregistré en tant que données d’élément, il est passé à la procédure de fenêtre conjointement avec les messages WM_MEASUREITEM et WM_DRAWITEM pour l’élément de menu correspondant. Le pointeur est contenu dans le membre itemData des structures MEASUREITEMSTRUCT et DRAWITEMSTRUCT .

Un message WM_MEASUREITEM est envoyé pour chaque élément de menu dessiné par le propriétaire la première fois qu’il s’affiche. L’application traite ce message en sélectionnant la police de l’élément de menu dans un contexte d’appareil, puis en déterminant l’espace nécessaire pour afficher le texte de l’élément de menu dans cette police. La police et le texte de l’élément de menu sont tous deux spécifiés par la structure de l’élément de MYITEM menu (la structure définie par l’application). L’application détermine la taille du texte à l’aide de la fonction GetTextExtentPoint32 .

La procédure de fenêtre traite le message WM_DRAWITEM en affichant le texte de l’élément de menu dans la police appropriée. La police et le texte de l’élément de menu sont tous deux spécifiés par la structure de l’élément de MYITEM menu. L’application sélectionne le texte et les couleurs d’arrière-plan appropriées à l’état de l’élément de menu.

La procédure de fenêtre traite le message WM_DESTROY pour détruire les polices et libérer de la mémoire. L’application supprime la police et libère la structure MYITEM définie par l’application pour chaque élément de menu.

Voici les parties pertinentes du fichier d’en-tête de l’application.

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

Voici les parties pertinentes de la procédure de fenêtre de l’application et de ses fonctions associées.

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

Utilisation de bitmaps de coche personnalisées

Le système fournit une bitmap de marque de case activée par défaut pour l’affichage en regard d’un élément de menu sélectionné. Vous pouvez personnaliser un élément de menu individuel en fournissant une paire de bitmaps pour remplacer la bitmap case activée marque par défaut. Le système affiche une bitmap lorsque l’élément est sélectionné et l’autre lorsqu’il est clair. Cette section décrit les étapes de création et d’utilisation de bitmaps de marque de case activée personnalisées.

Création d’images bitmap de coche personnalisées

Une bitmap de marque de case activée personnalisée doit avoir la même taille que la bitmap de marque case activée par défaut. Vous pouvez récupérer la taille de marque de case activée par défaut de la bitmap en appelant la fonction GetSystemMetrics. Le mot d’ordre inférieur de la valeur de retour de cette fonction spécifie la largeur ; le mot d’ordre supérieur spécifie la hauteur.

Vous pouvez utiliser des ressources bitmap pour fournir des bitmaps de marque case activée. Toutefois, étant donné que la taille de bitmap requise varie en fonction du type d’affichage, vous devrez peut-être redimensionner la bitmap au moment de l’exécution à l’aide de la fonction StretchBlt . Selon la bitmap, la distorsion causée par le dimensionnement peut produire des résultats inacceptables.

Au lieu d’utiliser une ressource bitmap, vous pouvez créer une bitmap au moment de l’exécution à l’aide de fonctions GDI.

Pour créer une bitmap au moment de l’exécution

  1. Utilisez la fonction CreateCompatibleDC pour créer un contexte d’appareil compatible avec celui utilisé par la fenêtre main de l’application.

    Le paramètre hdc de la fonction peut spécifier null ou la valeur de retour de la fonction. CreateCompatibleDC retourne un handle au contexte d’appareil compatible.

  2. Utilisez la fonction CreateCompatibleBitmap pour créer une bitmap compatible avec la fenêtre main de l’application.

    Les paramètres nWidth et nHeight de cette fonction définissent la taille de la bitmap ; ils doivent spécifier les informations de largeur et de hauteur retournées par la fonction GetSystemMetrics .

    Notes

    Vous pouvez également utiliser la fonction CreateBitmap pour créer une bitmap monochrome.

     

  3. Utilisez la fonction SelectObject pour sélectionner la bitmap dans le contexte de l’appareil compatible.

  4. Utilisez des fonctions de dessin GDI, telles que Ellipse et LineTo, pour dessiner une image dans la bitmap, ou utilisez des fonctions telles que BitBlt et StretchBlt pour copier une image dans la bitmap.

Pour plus d’informations, consultez Bitmaps.

Association de bitmaps à un élément de menu

Vous associez une paire de bitmaps case activée-mark à un élément de menu en passant les handles des bitmaps à la fonction SetMenuItemBitmaps. Le paramètre hBitmapUnchecked identifie la bitmap en clair, et le paramètre hBitmapChecked identifie la bitmap sélectionnée. Si vous souhaitez supprimer une ou les deux marques case activée d’un élément de menu, vous pouvez définir le paramètre hBitmapUnchecked ou hBitmapChecked, ou les deux, sur NULL.

Définition de l’attribut de coche

La fonction CheckMenuItem définit l’attribut de marque case activée d’un élément de menu sur sélectionné ou effacé. Vous pouvez spécifier la valeur de MF_CHECKED pour définir l’attribut case activée-mark sur sélectionné et la valeur MF_UNCHECKED pour le définir sur effacer.

Vous pouvez également définir l’état case activée d’un élément de menu à l’aide de la fonction SetMenuItemInfo.

Parfois, un groupe d’éléments de menu représente un ensemble d’options mutuellement exclusives. À l’aide de la fonction CheckMenuRadioItem, vous pouvez case activée un élément de menu tout en supprimant simultanément la marque case activée de tous les autres éléments de menu du groupe.

Simulation de cases à cocher dans un menu

Cette rubrique contient un exemple qui montre comment simuler case activée zones dans un menu. L’exemple contient un menu Caractère dont les éléments permettent à l’utilisateur de définir les attributs gras, italiques et soulignés de la police actuelle. Lorsqu’un attribut de police est en vigueur, une marque de case activée s’affiche dans la zone de case activée en regard de l’élément de menu correspondant ; sinon, une zone de case activée vide s’affiche en regard de l’élément.

L’exemple remplace la bitmap case activée-mark par défaut par deux bitmaps : une bitmap avec une zone de case activée sélectionnée et la bitmap par une zone vide. L’image bitmap de zone de case activée sélectionnée s’affiche en regard de l’élément de menu Gras, Italique ou Souligné lorsque l’attribut de marque case activée de l’élément est défini sur MF_CHECKED. L’image bitmap de zone de case activée vide ou vide s’affiche lorsque l’attribut case activée-mark a la valeur MF_UNCHECKED.

Le système fournit une bitmap prédéfinie qui contient les images utilisées pour les cases d’case activée et les cases d’option. L’exemple isole les zones de case activée sélectionnées et vides, les copie dans deux bitmaps distinctes, puis les utilise comme bitmaps sélectionnées et effacées pour les éléments du menu Caractère.

Pour récupérer un handle dans l’image bitmap de zone de case activée définie par le système, l’exemple appelle la fonction LoadBitmap, en spécifiant NULL comme paramètre hInstance et OBM_CHECKBOXES comme paramètre lpBitmapName. Étant donné que les images de la bitmap ont toutes la même taille, l’exemple peut les isoler en divisant la largeur et la hauteur de la bitmap par le nombre d’images dans ses lignes et colonnes.

La partie suivante d’un fichier de définition de ressource montre comment les éléments de menu dans le menu Caractère sont définis. Notez qu’aucun attribut de police n’est appliqué initialement, de sorte que l’attribut case activée-mark de l’élément Regular est défini sur sélectionné et, par défaut, l’attribut case activée-mark des éléments restants est défini sur effacer.

#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

Voici le contenu pertinent du fichier d’en-tête de l’application.

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

L’exemple suivant montre les parties de la procédure de fenêtre qui créent les bitmaps case activée-mark, définissent l’attribut case activée-mark des éléments de menu Bold, Italic et Soulignement et détruisent les bitmaps case activée-mark.

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

Exemple d’utilisation de bitmaps de coche personnalisées

L’exemple de cette rubrique affecte des bitmaps de marque case activée personnalisées aux éléments de menu dans deux menus. Les éléments de menu du premier menu spécifient des attributs de caractère : gras, italique et souligné. Chaque élément de menu peut être sélectionné ou effacé. Pour ces éléments de menu, l’exemple utilise des bitmaps de marque case activée qui ressemblent aux états sélectionnés et effacés d’un contrôle de zone de case activée.

Les éléments de menu du deuxième menu spécifient les paramètres d’alignement des paragraphes : gauche, centré et droit. Un seul de ces éléments de menu est sélectionné à tout moment. Pour ces éléments de menu, l’exemple utilise des bitmaps de marque case activée qui ressemblent aux états sélectionnés et effacés d’un contrôle de case d’option.

La procédure de fenêtre traite le message WM_CREATE en appelant la fonction OnCreate définie par l’application. OnCreatecrée les quatre bitmaps case activée-mark, puis les affecte à leurs éléments de menu appropriés à l’aide de la fonction SetMenuItemBitmaps.

Pour créer chaque bitmap, OnCreate appelle la fonction CreateMenuBitmaps définie par l’application, en spécifiant un pointeur vers une fonction de dessin spécifique à une bitmap. CreateMenuBitmaps crée une bitmap monochrome de la taille requise, la sélectionne dans un contexte de périphérique mémoire et efface l’arrière-plan. Ensuite, il appelle la fonction de dessin spécifiée pour remplir le premier plan.

Les quatre fonctions de dessin définies par l’application sont DrawCheck, DrawUncheck, DrawRadioCheck et DrawRadioUncheck. Ils dessinent un rectangle avec un X, un rectangle vide, une ellipse contenant une ellipse remplie plus petite et une ellipse vide, respectivement.

La procédure de fenêtre traite le message WM_DESTROY en supprimant les bitmaps de marque case activée. Il récupère chaque handle bitmap à l’aide de la fonction GetMenuItemInfo , puis transmet un handle à la fonction .

Lorsque l’utilisateur choisit un élément de menu, un message WM_COMMAND est envoyé à la fenêtre propriétaire. Pour les éléments de menu du menu Caractère , la procédure de fenêtre appelle la fonction CheckCharacterItem définie par l’application. Pour les éléments du menu Paragraphe , la procédure de fenêtre appelle la fonction CheckParagraphItem définie par l’application.

Chaque élément du menu Caractère peut être sélectionné et effacé indépendamment. Par conséquent, CheckCharacterItem bascule simplement l’état case activée de l’élément de menu spécifié. Tout d’abord, la fonction appelle la fonction GetMenuItemInfo pour obtenir l’état actuel de l’élément de menu. Ensuite, il bascule l’indicateur d’état MFS_CHECKED et définit le nouvel état en appelant la fonction SetMenuItemInfo .

Contrairement aux attributs de caractère, un seul alignement de paragraphe peut être sélectionné à la fois. Par conséquent, CheckParagraphItem vérifie l’élément de menu spécifié et supprime la marque case activée de tous les autres éléments du menu. Pour ce faire, elle appelle la fonction CheckMenuRadioItem .

Voici les parties pertinentes du fichier d’en-tête de l’application.

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

Voici les parties pertinentes de la procédure de fenêtre de l’application et des fonctions associées.

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