API Windows en C

Contrôles et messages

CGI

♪

- Titre : API Windows en C

- Auteur : CGI

- Parution : 30 mai 2005

- Licence : 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.

Sommaire

- I. Introduction

- II. Création d'un contrôle

- III. Les messages

- IV. Code complet

I - 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 multiligne. 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).

II - 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 où vous désiriez 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 multiligne 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.

III - 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. À 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. Ils sont omniprésents dans Windows. Le moindre événement qui survient et c'est un message qui est envoyé. Il est donc important d'être bien documenté, notamment 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 à laquelle il a été posté.
POINT  pt;          // Coordonnées du curseur au moment où il a été posté.
} MSG;

IV - 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++.

À vos PC.

CGi

Sommaire