Introduction :
Dans ce chapitre, nous allons revenir sur le traitement des messages.
Nous allons aussi créer un contrôle enfant que nous allons mettre sur la
fenêtre principale.
Ce contrôle sera un contrôle d'édition multi lignes. Il devra se positionner
sur toute la surface client de la fenêtre principale
(on appelle surface client d'une fenêtre toute sa partie intérieure, c'est-à-dire
la fenêtre sans sa bordure et sa barre de titre).
Création d'un contrôle :
Comme nous l'avons dit au
chapitre précédent
un contrôle est une fenêtre.
Nous allons donc utiliser comme pour la fenêtre principale la fonction
CreateWindow pour le créer.
HWND hEdit;
hEdit =CreateWindow("edit", "Texte",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL,
0, 0, 0, 0, hwnd, NULL, hinst, NULL);
Pour les contrôles, les classes de fenêtres sont globales et définies par Windows. Celle du contrôle
d'édition s'appelle : "EDIT". Nous la mettrons donc comme premier paramètre
de CreateWindow.
Son deuxième paramètre attend un pointeur sur une chaîne de
caractères au cas ou vous désirez qu'il contienne du texte dès sa création.
Le troisième attend les identificateurs de style. Ils sont ici complètement
différents de ce qu'on avait mis pour la fenêtre principale :
WS_VISIBLE rend la fenêtre visible dés sa création.
WS_CHILD en fait un contrôle enfant. C'est obligatoire si l'on veut qu'il soit à
l'intérieur de notre fenêtre.
ES_MULTILINE multi lignes si on veut avoir la possibilité de saisir plusieurs lignes.
ES_WANTRETURN autorise le retour à la ligne automatique.
WS_VSCROLL pour lui mettre un ascenseur vertical.
(Les identificateurs de style étant très nombreux, je vous propose de consulter l'aide Win32 API
CreateWindow, ou CreateWindowEx qui permet des styles supplémentaires).
Les quatre paramètres suivants sont comme pour la fenêtre principale, sa position et
ses dimensions. Dans le code complet de l'exemple, elles sont toutes mises à zéro car
le contrôle sera redimensionné automatiquement à chaque changement de dimensions
de la fenêtre principale (voir plus loin dans ce document).
Le paramètre suivant est le handle de la fenêtre principale car elle en est
le parent.
Et comme pour la fenêtre principale le handle d'instance de l'application
pour l'avant dernier paramètre.
Dans le code de l'exemple, pour des raisons de simplicité, j'ai créé une
variable globale pour le handle d'instance que j'ai initialisé dans la fonction
WinMain.
Les messages :
Nous avons dit que notre contrôle d'édition devait se redimensionner pour
occuper la surface client de la fenêtre principale. Mais comment savoir
que les dimensions de la fenêtre principale ont changé ? Par
l'intermédiaire d'un message ! Si un utilisateur a changé les dimensions de la fenêtre,
Windows va envoyer un message à cette fenêtre pour l'en informer.
Dans ce cas précis le message est WM_SIZE.
C'est à nous de traiter ce message dans la procédure de fenêtre :
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hEdit;
switch (uMsg)
{
case WM_CREATE:
hEdit =CreateWindow("edit", "Texte",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL,
0, 0, 0, 0, hwnd, NULL, hinst, NULL);
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 voir dans l'exemple nous créons le contrôle d'édition
à l'arrivée du message WM_CREATE, message envoyé par Windows quand la création
de la fenêtre principale est terminée, mais pas encore visible.
A la réception du message WM_SIZE, nous récupérons les dimensions de la surface
client de la fenêtre dans le paramètre lParam joint au message.
La largeur étant dans son mot de poids faible et la hauteur dans son mot
de poids fort. Nous les extrayons à l'aide des macros LOWORD et HIWORD.
Et nous affecterons ces dimensions au contrôle d'édition à l'aide de la fonction
MoveWindow (son dernier paramètre à TRUE informe Windows qu'il doit redessiner
le contrôle après l'appel de la fonction).
Son premier paramètre étant le handle du contrôle d'édition, car c'est lui dont on change
les dimensions.
Il est évident que l'on ne va pas passer tous les messages en revue, vous
pouvez pour cela consulter l'aide sur L'API Win32. Cet exemple vous donne
déjà une idée de la manière dont on intercepte les événements dans Windows.
Nous aurons l'occasion de voir et revoir les messages dans les chapitres suivants.
Il sont omniprésent dans Windows. Le moindre événement qui survient et c'est un message
qui est envoyé.
Il est donc important d'être bien documenté, notament sur les divers messages existants.
Et ils sont nombreux.
Comme dit au chapitre précédent, les messages non traités doivent l'être
par la fonction DefWindowProc. C'est une procédure de fenêtre interne
qui traite les messages afin de faire le minimum vital pour la fenêtre.
Ne serait ce que simplement l'afficher.
Structure MSG :
typedef struct tagMSG {
HWND hwnd; // Handle de la fenêtre de destination.
UINT message; // Le message proprement dit.
WPARAM wParam; // Paramètre dépendant du message.
LPARAM lParam; // Paramètre dépendant du message.
DWORD time; // Heure ou il a été posté.
POINT pt; // Coordonnées du curseur au moment ou il a été posté.
} MSG;
Code complet :
#include <windows.h>
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;
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;
hwnd = CreateWindow("MaWinClass", "Titre", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, 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;
switch (uMsg)
{
case WM_CREATE:
hEdit =CreateWindow("edit", "Texte",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL,
0, 0, 0, 0, hwnd, NULL, hinst, NULL);
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);
}
}
Dans le code de cet exemple j'ai mis le pinceau à NULL
(wc.hbrBackground = NULL) dans la classe de fenêtre de la fenêtre principale,
sinon un il y a scintillement désagréable sur l'ascenseur du contrôle d'édition
lors des redimensionnements.
Ce code a été testé sous VC++, CodeBlocks(Mingw), C++ Builder, DevC++.
A vos PC.
CGi
Avec la contribution de Bestiol pour la relecture.
|