3 - Les commandes et notifications

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Dans ce chapitre nous allons aborder un message particulier : WM_COMMAND. Ce message sert à envoyer des commandes. Une commande est envoyée suite à l'action d'un contrôle par l'utilisateur. C'est-à-dire par exemple le clic d'un bouton ou d'une option d'un menu… Nous devons donc l'intercepter pour implémenter l'action que nous voulons faire. Dans l'exemple nous créerons un menu qui va nous permettre d'envoyer des commandes utilisateur aux fenêtres de l'application.
Nous verrons aussi qu'un contrôle peut envoyer des informations à l'application avec ce message quand des événements propres à lui-même se produisent. On les appelle des notifications.
Nous aborderons aussi l'envoi de messages par programme.

Image non disponible

II. Création du menu

Pour préparer notre exemple, nous allons d'abord créer le menu. Il existe deux solutions pour créer des menus, soit par programme, soit à l'aide de ressources. Les ressources feront l'objet d'un autre chapitre. Nous allons donc utiliser la première solution (à savoir qu'il est plus courant d'utiliser les ressources pour construire un menu).

 
Sélectionnez
     #define IDM_QUIT 1





     HMENU hMenu, hSousMenu;



     hSousMenu = CreateMenu();

     AppendMenu(hSousMenu, MF_STRING, IDM_QUIT, "Quitter");

     hMenu  = CreateMenu();

     AppendMenu(hMenu,MF_POPUP,(UINT)hSousMenu,"Fichier");

Les menus sont créés avec la fonction CreateMenu qui renvoie un handle de menu (HMENU). Dans l'exemple nous créons un menu principal et un sous-menu. Nous créons d'abord le sous-menu, car il devra être ajouté au menu principal. Nous lui ajoutons une option de menu à l'aide de la fonction AppendMenu. Nous pouvons utiliser cette fonction autant de fois que l'on a d'options à rajouter. Elle reçoit comme paramètres :
le handle de menu auquel on veut l'ajouter.
Une constante indiquant son aspect (MF_STRING, car c'est une chaîne de caractères).
L'identificateur de l'option qui la distinguera parmi les autres. C'est une constante de type UINT (unsigned int) que l'on peut nommer pour plus de clarté dans le code (#define IDM_QUIT 1).
Le dernier paramètre est un pointeur sur la chaîne de caractères représentant l'option visuellement dans le menu ("Quitter" dans l'exemple).
Nous créons ensuite le menu principal de la même façon. Nous lui ajoutons le sous-menu créé précédemment toujours avec la fonction AppendMenu en lui passant le handle du sous-menu comme paramètre (troisième paramètre de AppendMenu). On lui donnera un aspect de type popup (deuxième paramètre de AppendMenu à MF_POPUP).
Maintenant, il faut l'ajouter à la fenêtre principale de l'application. Dans l'exemple nous le ferons lors de la création de la fenêtre en passant son handle comme paramètre à la fonction

 
Sélectionnez
CreateWindow :
    hwnd = CreateWindow("MaWinClass", "Titre", WS_OVERLAPPEDWINDOW,

                                   CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,

                                                   NULL, hMenu, hinstance, NULL);

III. Les commandes

Comment savoir que l'utilisateur a actionné l'option du menu ?
Et bien en traitant le message WM_COMMAND. Comme nous l'avons dit plus haut, ce message est envoyé par Windows quand une commande utilisateur a été actionnée.
Mais comment savoir que cette commande vient d'une option particulière d'un menu ?
Dans le mot de poids faible du paramètre wParam qui est envoyé avec le message WM_COMMAND : LOWORD(wParam). Il contient l'identificateur que l'on avait attribué à l'option de menu lors de son ajout au menu (IDM_QUIT dans l'exemple) :

 
Sélectionnez
    switch (uMsg)

    {

        case WM_COMMAND :

            if(LOWORD(wParam) == IDM_QUIT) PostMessage(hwnd, WM_CLOSE,0,0);

            return 0;

Il suffit alors de faire un test sur ce paramètre si l'on est en train de traiter un message WM_COMMAND et d'agir en conséquence.

IV. Envoyer des messages

Dans cet exemple si l'on a affaire à la commande IDM_QUIT nous demanderons à la fenêtre de se fermer en lui envoyant un message WM_CLOSE avec la fonction PostMessage. Eh oui, nous aussi nous pouvons envoyer des messages !
PostMessage permet de déposer des messages dans la file d'attente.
Son premier paramètre est le handle de la fenêtre de destination.
Son second paramètre est l'identificateur du message (WM_CLOSE dans l'exemple).
Les deux paramètres suivants sont les valeurs des champs wParam et lParam du message (wParam et lParam sont inutilisés pour le message WM_CLOSE).
Il existe une autre fonction pour envoyer des messages : SendMessage. Elle reçoit les mêmes paramètres que PostMessage et dans le même ordre. La différence est qu'elle envoie le message directement à la procédure de fenêtre sans passer par la file d'attente. Ensuite, elle attend que le message ait été traité, afin de retourner un résultat. Dans l'exemple nous l'utilisons pour ordonner au contrôle d'édition de changer sa police de caractères :

 
Sélectionnez
             HFONT hFont;



             hFont = (HFONT)GetStockObject(ANSI_FIXED_FONT);

             SendMessage(hEdit,WM_SETFONT,(UINT)hFont,TRUE);

Nous avons ici récupéré le handle d'une police de caractères prédéfinie de Windows avec la fonction GetStockObject (cette fonction permet de récupérer des objets initialisés au démarrage de Windows, tels des pinceaux, crayons...).
Nous allons lui envoyer un second message pour mettre ses marges intérieures droite et gauche à 5 pixels :

 
Sélectionnez
SendMessage(hEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN,

                                                                MAKELONG(5, 5));

La valeur des marges est envoyée dans un seul paramètre. Il est reconstitué en un DWORD avec la macro MAKELONG. Vous avez dû remarquer que le nom de ce message est préfixé des lettres EM au lieu de WM. C'est tout simplement qu'il s'agit d'un message destiné spécifiquement aux contrôles d'édition. EM comme Edit Message et bien sûr WM comme Window Message qui eux s'adressent à tous les types de fenêtres.

V. Les notifications

Pendant le déroulement du programme, les contrôles envoient des messages WM_COMMAND à leur fenêtre parent. Ceci quand ils subissent certains événements. Prenons comme exemple le contrôle d'édition, à chaque fois que vous modifiez son texte, il envoie un message WM_COMMAND avec le paramètre EN_CHANGE, pour nous le notifier. On les appelle les notifications. Elles sont envoyées dans le mot de poids fort du champ wParam : HIWORD(wParam) du message WM_COMMAND. Je vous recommande de consulter l'aide API Win32 pour chaque contrôle, afin vous documenter sur leurs différentes notifications.

 
Sélectionnez
    switch (uMsg)

    {

        case WM_COMMAND :

            if(HIWORD(wParam) == EN_CHANGE) EditNotChg = FALSE;

            return 0;

Dans l'exemple, à la réception de cette notification nous mémoriserons dans une variable ce changement. Variable que nous testerons à la fermeture de la fenêtre principale afin de proposer une boîte de message pour laisser le choix à l'utilisateur de fermer ou non la fenêtre s'il y a eu modification du texte du contrôle d'édition :

 
Sélectionnez
    switch (uMsg)

    {

        case WM_CLOSE :

            if(EditNotChg ||

               MessageBox(hwnd,"Le texte a été modifié.\r\nÊtes-vous sûr de \

vouloir fermer l'application ?"

                            ,"Question ?",MB_YESNO | MB_ICONQUESTION ) == IDYES)

                                                            DestroyWindow(hwnd);

            return 0;

Nous interceptons pour cela le message WM_CLOSE. Nous testons la variable EditNotChg si elle est à TRUE, DestroyWindow est appelé, ce qui a pour conséquence d'envoyer un message WM_DESTROY et donc de fermer l'application. Si EditNotChg est à FALSE la fonction MessageBox est appelée si elle retourne IDYES, nous appelons de même DestroyWindow, sinon le programme continue.
MessageBox, vous l'avez compris sert à appeler des boîtes de message. Son premier paramètre est le handle de fenêtre de son parent. Le deuxième paramètre est le texte du message ou de la question. Le troisième paramètre est son titre. Le quatrième est une constante indiquant son type. (MB_YESNO | MB_ICONQUESTION) dans l'exemple, car elle possède un bouton "Oui", un bouton "Non" et l'icône de question (voir l'aide API Win32 pour les autres possibilités).

VI. Code complet

 
Sélectionnez
#include <windows.h>



#define IDM_QUIT 1



HINSTANCE hinst;



LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);



int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,

                                                LPSTR lpCmdLine, int nCmdShow)

{

    HWND hwnd;

    MSG msg;

    WNDCLASS wc;

    HMENU hMenu, hSousMenu;



    hinst = hinstance;



    wc.style = 0 ;

    wc.lpfnWndProc = MainWndProc;

    wc.cbClsExtra = 0;

    wc.cbWndExtra = 0;

    wc.hInstance = hinstance;

    wc.hIcon = NULL;

    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    wc.hbrBackground = NULL;

    wc.lpszMenuName =  NULL;

    wc.lpszClassName = "MaWinClass";



    if(!RegisterClass(&wc)) return FALSE;



    hSousMenu = CreateMenu();

    AppendMenu(hSousMenu, MF_STRING, IDM_QUIT, "Quitter");

    hMenu  = CreateMenu();

    AppendMenu(hMenu,MF_POPUP,(UINT)hSousMenu,"Fichier");



    hwnd = CreateWindow("MaWinClass", "Titre", WS_OVERLAPPEDWINDOW,

                                   CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,

                                                   NULL, hMenu, hinstance, NULL);

    if (!hwnd)  return FALSE;



    ShowWindow(hwnd, nCmdShow);



    while (GetMessage(&msg, NULL, 0, 0))

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

    return msg.wParam;

}

/******************************************************************************/



LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    static HWND hEdit;

    static BOOL EditNotChg = TRUE;

    

    switch (uMsg)

    {

        case WM_CREATE:

            {

             HFONT hFont;

             hEdit = CreateWindow("edit", "Texte",

               WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL,

                                            0, 0, 0, 0, hwnd, NULL, hinst, NULL);

             hFont = (HFONT)GetStockObject(ANSI_FIXED_FONT);

             SendMessage(hEdit,WM_SETFONT,(UINT)hFont,TRUE);

             SendMessage(hEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN,

                                                                MAKELONG(5, 5));

             return 0;

            }



        case WM_CLOSE :

            if(EditNotChg ||

               MessageBox(hwnd,"Le texte a été modifié.\r\nÊtes-vous sûr de \

vouloir fermer l'application ?"

                            ,"Question ?",MB_YESNO | MB_ICONQUESTION ) == IDYES)

                                                            DestroyWindow(hwnd);

            return 0;



        case WM_COMMAND :

            if(LOWORD(wParam) == IDM_QUIT) PostMessage(hwnd, WM_CLOSE,0,0);

            if(HIWORD(wParam) == EN_CHANGE) EditNotChg = FALSE;

            return 0;



        case WM_SIZE :

            MoveWindow(hEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);

             return 0;            



        case WM_DESTROY :

            PostQuitMessage(0);

            return 0;



        default :

            return DefWindowProc(hwnd, uMsg, wParam, lParam);

    }

}

Comme vous pouvez le constater, les messages sont omniprésents dans Windows.
Ce code a été testé sous VC++, CodeBlocks(Mingw), C++ Builder, DevC++.

À vos PC.

CGi

Sommaire

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

C/C++
  Les pointeurs du C/C++.   Les listes chaînées.             Liste simple.             Liste triée.             Liste double.   Les arbres.   Les tas.   Le C orienté objets ?
  1 - La fenêtre principale.   2 - Contrôles et messages.   3 - Les commandes.   4 - Dialogue std.   5 - Contexte de périph.   6 - Dessiner.   7 - Les ressources.   8 - Dialogue perso.   9 - Dialogue comm.   10 - Les accélérateurs.
C++ BUILDER
  Trucs et astuces.   Composant.   TRichEdit.   TDrawGrid.   Application MDI.   TThread.   wxWidgets.   Style Win XP.
  Première application.   Construire un menu.   Dessiner.   Sisers, Timers...   Dialogues standards.   Dialogues perso.
DotNet
  Composant C# Builder.   Contrôle WinForm.   Application MDI.
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 CGI. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.