Introduction :
Dans ce chapitre nous allons aborder un message particulier : WM_COMMAND.
Ce message sert à envoyer des commandes.
Une commande est envoyé suite à l'action d'un contrôle par l'utilisateur.
C'est-à-dire par exemple le click 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 propre à lui même se produisent.
On les appelle des notifications.
Nous aborderons aussi l'envoi de messages par programme.
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).
#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ère).
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 CreateWindow :
hwnd = CreateWindow("MaWinClass", "Titre", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, hMenu, hinstance, NULL);
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) :
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 consequence.
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. Et 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
ai é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ère :
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ère 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, tel des pinceaux, crayons...).
Nous allons lui envoyer un second message pour mettre ses marges interieures
droite et gauche à 5 pixels :
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 du remarquer que le nom de ce message est préfixé des lettre 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'adresse à tous les types de fenêtres.
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.
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 :
switch (uMsg)
{
case WM_CLOSE:
if(EditNotChg ||
MessageBox(hwnd,"Le texte a été modifié.\r\nEtes 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 messages.
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 autre possibilités).
Code complet :
#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\nEtes 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 pouver le constater les message sont omniprésent dans Windows.
Ce code a été testé sous VC++, CodeBlocks(Mingw), C++ Builder, DevC++.
A vos PC.
CGi
Avec la contribution de nico-pyright(c) pour la relecture.
|