Tutorial 8: Le Menu

Dans ce tutorial, nous allons apprendre comment incorporer un menu à notre fenêtre.
Downloadez l'example 1 et l'example2.

Theorie:

Le menu est un des composants le plus important d'une votre fenêtre. Le menu présente la liste des services qu'un programme peut offrir à l'utilisateur. L'utilisateur ne doit pas lire le manuel inclus avec le programme pour être capable de l'employer, il lui suffit de lire attentivement le menu pour obtenir une vue d'ensemble de la capacité d'un programme particulier et commencer à jouer avec lui immédiatement. Puisque un menu est un outil pour que l'utilisateur puisse rapidement se diriger, vous devez suivre la norme. Succinctement, les deux premiers articles du menu doivent être : le fichier et d'Edition et le fichier d'Aide. Vous pouvez insérer vos propres articles de menu entre celui d'Edition et celui de l'Aide. Si un article du menu invoque une boîte de dialogue, vous devez ajouter un ellipsis (...) à la chaîne de caractère du menu.
Le menu est une sorte de ressource. Il y a plusieurs sortes de ressources tels que la boîte de dialogue, les tableaux de caractères, l'icône, bitmap, le menu etc. Les ressources sont décrites dans un fichier séparé, appelé fichier de ressources et qui normalement a l'extension .rc. Vous combinez alors les ressources avec le code source pendant l'étape de liaison. Le produit final est un fichier exécutable qui contient, à la fois les instructions et les ressources.
Vous pouvez écrire des scénarios de ressources en employant n'importe quel éditeur de texte. Ces fichiers ressources sont composés des expressions qui décrivent les convenances ainsi que d'autres attributs des ressources employées dans un programme particulier. Bien que vous puissiez écrire des scénarios de ressource avec un éditeur de texte, c'est plutôt encombrant. Une meilleure alternative est d'employer un éditeur de ressources qui vous laisse concevoir visuellement des ressources avec un bien meilleurs confort. Les éditeurs de ressources sont habituellement inclus dans le pack avec le compilateur comme C Visuel +, Borland C +, etc.
Nous pouvons décrire une ressource de menu comme ceci :
  Les programmeurs sous C peuvent se rendre compte que c'est semblable à la déclaration de la structure MyMenu d'un nom de menu suivi par le mot clef MENU et le menu est inscrit entre parenthèses[]. Alternativement, vous pouvez employer 'BEGIN' et 'END' au lieu des parenthèses si vous préférez. Cette syntaxe est plus acceptable pour les programmeurs en Pascal.
La liste du menu peut être soit MENUITEM soit la déclaration POPUP.
La déclaration
MENUITEM définit une barre(bar) de menu qui n'invoque pas de menu popup lorsqu'il est choisi. La syntaxe est la suivante : Il commence par le mot-clé MENUITEM suivi du texte que vous voulez employer en tant que chaîne de caractères pour la barre de menu. Notez l'esperluète. Il sert à souligner le caractère qui le suit. Après la chaîne de caractère formant le texte on trouve le n°ID de l'article du menu. L'ID est le numéro qui sera employé pour identifier l'article du menu dans le message envoyé à la procédure de fenêtre lorsqu'on le sélectionne. Chaque n°ID du menu doit être unique vis à vis des autres.
'Options' justement en option. Les options disponibles sont les suivantes :
Vous pouvez employer une de ces options ou bien les combiner entre elles avec l'opérateur "or". Prenez garde à ce que INACTIVE et GRAYED ne soient pas utilisés ensembles.
La syntaxe de la déclaration POPUP est la suivante :
 
POPUP "&text" [,options]
{
  [menu list]
}
La déclaration POPUP définit une barre de menu qui, quand elle est choisi, abaisse une liste d'articles de menu dans une petite fenêtre POPUP. La liste du menu peut être un MENUTIEM ou la déclaration POPUP. Il y a une sorte de déclaration spéciale qui est MENUITEM, MENUITEM est le SÉPARATEUR, qui dessinera une ligne horizontale à l'intérieur de la fenêtre POPUP.
L'étape suivante après que vous en ayez finis avec le scénario de ressources est que votre programme doit faire référence à votre menu.
Vous pouvez faire ceci à deux endroits différents dans votre programme. Donc vous pouvez vous demander, quelle est la différence entre ces deux méthodes ?
Quand vous faites référence au menu dans la structure de WNDCLASSEX, le menu devient le menu "par défaut" pour la classe de fenêtre. Chaque fenêtre de cette classe aura le même menu.
Si vous souhaitez que chaque fenêtre ait la même classe mais qu'elles aient des menus différents, vous devez choisir la deuxième forme. Dans ce cas, n'importe quelle fenêtre dont on passe l'handle de son menu à la fonction CreateWindowEx aura un menu qui "ne tient pas compte" du menu par défaut, défini dans la structure WNDCLASSEX.
Ensuite nous devons examiner comment un menu fait pour indiquer à la procédure de fenêtre quand l'utilisateur choisit un des article du menu.
Quand l'utilisateur choisit un article du menu, la procédure de fenêtre reçoit le message WM_COMMAND. Le mot de poids faible de wParam contient le n°ID de l'article choisi dans le menu. (Le mot de poids faible de wParam = si c'est du contenu de eax (qui est sur 32Bit) dont on parle alors on parle plus précisément ici des 16 dernier bits de eax).
Maintenant nous avons les informations suffisantes pour créer et employer un menu. On va le faire.

Example:

Le premier exemple montre comment créer et employer un menu en spécifiant le nom de menu dans la classe de fenêtre.

.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0
MenuName db "FirstMenu",0                ; Le nom de notre menu dans le fichier de ressources.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.const
IDM_TEST equ 1                    ; (Menu IDs) ou: Les n°Identification des éléments constituants le Menu
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke GetCommandLine
    mov CommandLine,eax

    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,OFFSET MenuName        ; Place le nom de notre menu ici
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .WHILE TRUE
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke DispatchMessage, ADDR msg
    .ENDW
    mov     eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_COMMAND
        mov eax,wParam
        .IF ax==IDM_TEST
            invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
        .ELSEIF ax==IDM_HELLO
            invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
        .ELSEIF ax==IDM_GOODBYE
            invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
        .ELSE
            invoke DestroyWindow,hWnd
        .ENDIF
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp
end start
**************************************************************************************************************************

Menu.rc
**************************************************************************************************************************

#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4

FirstMenu MENU
{
 POPUP "&PopUp"
        {
         MENUITEM "&Say Hello",IDM_HELLO
         MENUITEM "Say &GoodBye", IDM_GOODBYE
         MENUITEM SEPARATOR
         MENUITEM "E&xit",IDM_EXIT
        }
 MENUITEM "&Test", IDM_TEST
}
 




Analyse:

On va analyser le fichier de ressources d'abord.
 
#define IDM_TEST 1                /* cette ligne revient au même que : IDM_TEST equ 1*/
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
 
Les susdites lignes définissent les n°ID des éléments constituants le menu. Vous pouvez assigner n'importe quelle valeur à l'ID tant que cette valeur est unique dans le menu.

FirstMenu MENU

Déclarez votre menu avec le mot clef 'MENU'

 POPUP "&PopUp"
        {
         MENUITEM "&Say Hello",IDM_HELLO
         MENUITEM "Say &GoodBye", IDM_GOODBYE
         MENUITEM SEPARATOR
         MENUITEM "E&xit",IDM_EXIT
        }

Définit un menu popup avec quatre articles de menu, le troisième est un séparateur de menu.

 MENUITEM "&Test", IDM_TEST

Définit une barre de menu dans le menu principal.
Ensuite nous examinons la source du code.
 

MenuName db "FirstMenu",0                ; C'est le nom de notre menu dans le fichier de ressources.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
 
MenuName est le nom du menu dans le fichier de ressources. Remarquez que vous pouvez définir plus qu'un menu dans le fichier de ressource. Donc vous devez spécifier quel menu vous voulez employer. Ces trois lignes définissent les chaînes de caractères à afficher dans les boîtes de message (Message Box) qui sont invoquées plus loin quand l'article approprié du menu est choisi par l'utilisateur.
 
IDM_TEST equ 1                    ; (Menu IDs) Les n° d'identités des éléments constituants le Menu.
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
 
Définit les 'menu IDs' pour pouvoir les utiliser dans la procédure de fenêtre. Ces valeurs DOIVENT ÊTRE identiques à celles définies dans le fichier de ressources.

    .ELSEIF uMsg==WM_COMMAND
        mov eax,wParam
        .IF ax==IDM_TEST
            invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
        .ELSEIF ax==IDM_HELLO
            invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
        .ELSEIF ax==IDM_GOODBYE
            invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
        .ELSE
            invoke DestroyWindow,hWnd
        .ENDIF

Dans la procédure de fenêtre, nous traitons des messages WM_COMMAND. Quand l'utilisateur choisit un article du menu, le menu ID de cet article est envoyé à la procédure de fenêtre dans le mot de poids faible de wParam grâce au message WM_COMMAND. Ainsi quand nous stockons la valeur de wParam dans eax, on compare sa valeur avec eax au niveau des 'menu IDs' que nous avons définis précédemment et le programme agit en conséquence. Dans les trois premiers cas, quand l'utilisateur sélectionne les articles Test, Say Hello, and Say GoodBye du menu, uniquement une seule chaîne de caractères s'affiche dans une boîte de message.
Si l'utilisateur choisit l'article 'Exit' dans le menu, nous appelons DestroyWindow avec l'handle de notre fenêtre comme paramètre pour qu'il puisse fermer notre fenêtre.
Comme vous pouvez le voir, spécifier le nom du menu dans une classe de fenêtre est très facile. Cependant vous pouvez aussi employer une méthode supplémentaire pour charger un menu dans votre fenêtre. Je ne vous montrerai pas le code source entier ici. Le fichier de ressources est le même pour les deux méthodes. Il y a seulement quelques changements mineurs du fichier source que je vais vous montrer ci-dessous.
 

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HMENU ?                    ; handle of our menu
 
Définit une variable de type HMENU pour stocker notre menu handle.

        invoke LoadMenu, hInst, OFFSET MenuName
        mov    hMenu,eax
        INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,\
           hInst,NULL

Avant l'appel CreateWindowEx, nous appelons LoadMenu avec l'Instance Handle et un pointeur sur le nom de notre menu. LoadMenu renvoie l'handle de notre menu dans le fichier de ressources que nous passons à CreateWindowEx.


[Iczelion's Win32 Assembly HomePage]


Traduit par Morgatte