Programmer en C++
Win32 - Les menus

1. Création des fichiers nécessaires

Il y a d'autres méthodes existantes mais je trouve celle-ci facile.

Commençons par créer un projet "Win32" comme expliqué dans "Création d'une application Window de base". J'ai simplement appelé ce projet : "Menu".

Nous allons ajouter deux fichier supplémentaires à notre projet. Pour celà il suffit de cliquer sur "File", puis "New", puis sur "Empty file" (ou sur CTRL + SHIFT + N).

Validez "oui" à la question : "Do you want to add...", et ok à la fenêtre suivante.

Dans la fenêtre "Save file", nous nommerons le premier fichier "Constantes.h", puis en recommençant les opérations précédentes, nous nommerons le deuxième "Ressources.rc". Bien vérifier que le type de fichier choisi correspond à "All file (*.*)" pour pouvoir imposer l'extension du fichier ".h, et .rc".


Si tout c'est bien passé, les deux fichiers apparaîssent maintenant dans la fenêtre "Management", onglet "Projects".

Notez que deux dossiers ont été automatiquement créé pour contenir vos nouveaux fichiers.

2. Ecriture du code

2.1. Fichier "Constantes.h"

Nous allons utiliser une constante pour identifier le menu "Quitter". Voici le code à écrire dans le fichier "Constantes.h" :

#ifndef CONSTANTES_H
#define CONSTANTES_H


// Mes constantes :
#define IDM_EXIT 100

#endif // CONSTANTES_H

Tout ce que nous devons savoir ici, c'est que nous avons créé une constante "IDM_EXIT", et que nous lui avons attribué la valeur 100. Peu importe le nombre, il faut juste faire attention à ce qu'il ne soit pas déjà attribué à une autre constante.
Les autres lignes de code sont destinées à forcer le programme à ne lire qu'une fois notre fichier de constantes.

2.2. Fichier "Ressources.rc"

Le coeur de notre menu est à écrire dans le fichier "Ressources.rc". Voici le code :

#include <windows.h>
#include "Constantes.h"


ID_MENU MENU
BEGIN
    POPUP "&Fichier"
    BEGIN
        MENUITEM "&Quitter"
, IDM_EXIT
    END
END

2.3. Fichier "main.cpp"

C'est dans cette partie que nous allons faire comprendre a notre fenêtre windows, qu'elle possède maintenant un menu, et aussi de le faire apparaître.

Vous vous souvenez de la fonction "WinMain" ? Elle contient cette ligne qui précise l'absence de menu à notre fenêtre. Le pointeur ne contient aucune adresse (NULL).

wincl.lpszMenuName = NULL;

Nous allons donc la remplacer par ce nouveau code :

wincl.lpszMenuName = "MENU";     // Il y a un menu maintenant

On attribue donc a notre pointeur le terme "MENU" que nous avons choisi dans le fichier "Ressources.rc".

Juste après la fonction "CreateWindowEx()", qui nous permet d'obtenir un "handle" sur la fenêtre, nous insérons la ligne suivante :

SetMenu(hwnd, LoadMenu(hInstance, "ID_MENU"));

Cette fonction charge le menu dans la fenêtre. Elle nécessite une instance que nous allons définir en début de programme, juste après les "#include".

HINSTANCE hInstance;

La dernière chose qu'il ne faut pas oublier c'est de prévenir le programme principal qu'il existe un fichier "Constantes.h". Il nous faut donc ajouter un "include" qui s'ajoutera à ceux déjà existants.

#include "Constantes.h"

Voilà c'est un peu long a lire tout ça, mais regardez ce n'est juste que 4 lignes à écrire ou modifier.
Regardons déjà ce que ça donne :

C'est joli, et lorsque je clique sur "Fichier", le sous menu "Quitter" apparaît. Toutefois il nous reste une chose a faire car le menu "Quitter" ne réagit pas. Voyons comment arranger celà dans la dernière partie.

3. Comment faire réagir le menu

C'est cette fois dans la boucle des messages que nous allons intervenir. Nous allons intercepter le message "WM_COMMAND" généré par le clic de souris sur le menu "Quitter".
Le code à écrire est le suivant :

case WM_COMMAND:
    if(LOWORD(wParam) == IDM_EXIT)
    {
        SendMessage(hwnd, WM_DESTROY, 0, 0);
    }
    break;

Ci-dessous le code complet du fichier "main.cpp".

#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif


#include <tchar.h>
#include <windows.h>

#include "Constantes.h"

// Handle d'instance pour la création du menu
HINSTANCE hInstance;

// Déclaration de la procédure de fenêtre
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

// Un peu de couleur pour le fond de ma fenêtre
HBRUSH CouleurFond = CreateSolidBrush(RGB(255, 220,150));

// Création d'une variable globale contenant le nom de la classe de fenêtre
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    HWND hwnd;     // "handle" de la fenêtre
    MSG messages;     // variable de sauvegarde des messages provenant de la fenêtre
    WNDCLASSEX wincl;     // Création d'une structure de classe de fenêtre

    /* Structure de la classe de fenêtre */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Choix par défaut des icones et du pointeur de souris */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = "MENU";     // Il y a un menu maintenant (voir "Ressources.rc")
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    /* On charge ici notre jolie couleur pour le fond de la fenêtre */
    wincl.hbrBackground = CouleurFond;

    /* Enregistrement de la classe de fenêtre. Si échec, on quitte le programme */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* Création de la fenêtre */
    hwnd = CreateWindowEx (
        0,
        szClassName,
        _T("Ma jolie fenêtre"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        544,
        375,
        HWND_DESKTOP,
        NULL,
        hThisInstance,
        NULL
       );

    SetMenu(hwnd, LoadMenu(hInstance, "ID_MENU"));

    /* Affichage de la fenêtre à l'écran */
    ShowWindow (hwnd, nCmdShow);

    /* Boucle de lecture des messages */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Convertit les codes messages en caractères */
        TranslateMessage(&messages);
        /* Envoie des messages à la fonction WindowProcedure */
        DispatchMessage(&messages);
    }

    /* Valeur de retour */
    return messages.wParam;
}

/* Procédure de fenêtre (action à réaliser) */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_COMMAND:
            if(LOWORD(wParam) == IDM_EXIT)
            {
                SendMessage(hwnd, WM_DESTROY, 0, 0);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage (0);     // Déclenche la fermeture de la fenêtre
            break;
        default:
            return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
    return 0;
}

Voilà c'est terminé pour cette leçon.