II. Les fenêtres▲
II-A. Considérations générales▲
Les étapes à suivre pour créer une fenêtre sont les suivantes :
- Enregistrer une classe (ou modèle) de fenêtre
- Créer une fenêtre (... à partir d'un modèle existant)
- L'afficher (en effet, la fenêtre est initialement invisible)
- Intercepter tous les messages (souris, clavier, etc.) puis les passer à la procédure de fenêtre.
Une procédure de fenêtre est une fonction chargée de traiter les messages reçus.
II-B. Enregistrer une classe de fenêtre▲
L'enregistrement d'une nouvelle classe de fenêtre peut se faire avec la fonction RegisterClass.
Cette fonction nécessite comme paramètre l'adresse d'une structure de type WNDCLASS.
Exemple :
WNDCLASS wc;
wc.cbClsExtra =
0
;
wc.cbWndExtra =
0
;
wc.hbrBackground =
(
HBRUSH)(
COLOR_WINDOW +
1
);
wc.hCursor =
LoadCursor
(
NULL
, IDC_ARROW);
wc.hIcon =
LoadIcon
(
NULL
, IDI_APPLICATION);
wc.hInstance =
<
Instance de notre application>
;
wc.lpfnWndProc =
<
Adresse d-
une procédure de fenêtre>
;
wc.lpszClassName =
"
Classe 1
"
;
wc.lpszMenuName =
NULL
;
wc.style =
CS_HREDRAW |
CS_VREDRAW;
RegisterClass
(&
wc);
II-C. Créer une fenêtre, puis l'afficher▲
Après avoir enregistré une classe de fenêtre, on peut désormais créer une fenêtre.
HWND hWnd;
hWnd =
CreateWindow
(
"
Classe 1
"
, /* Classe de la fenêtre */
"
Notre première fenêtre
"
, /* Titre de la fenêtre */
WS_OVERLAPPEDWINDOW, /* Style de la fenêtre */
100
, /* Abscisse du coin supérieur gauche */
100
, /* Ordonnée du coin supérieur gauche */
600
, /* Largeur de la fenêtre */
300
, /* Hauteur de la fenêtre */
NULL
, /* Fenêtre parent */
NULL
, /* Menu */
<
Instance de notre application>
,
NULL
/* Paramètres additionnels */
);
Le paramètre style peut être une ou une combinaisons de constantes parmi lesquelles :
Constante | Description |
---|---|
WS_POPUP | Fenêtre pop-up (fenêtre "nue") |
WS_BORDER | Fenêtre comportant une bordure |
WS_CAPTION | Fenêtre avec barre de titre (inclut le style WS_BORDER) |
WS_MINIMIZEBOX | Fenêtre avec un bouton Réduire |
WS_MAXIMIZEBOX | Fenêtre avec un bouton Agrandir |
WS_SYSMENU | Fenêtre avec menu système (+ bouton Fermer) |
WS_SIZEBOX (ou WS_THICKFRAME) | Fenêtre redimensionnable |
WS_OVERLAPPED (ou WS_TILED) | Fenêtre recouvrable |
WS_OVERLAPPEDWINDOW | Combine tous les styles ci-dessus ! |
WS_CHILD | Fenêtre enfant (fenêtre dans une fenêtre) |
WS_VISIBLE | Fenêtre initialement visible |
Ensuite on affiche la fenêtre :
ShowWindow
(
hWnd, <
ShowCmd>
);
Où <ShowCmd> est un entier censé indiquer la manière dont comment on souhaite afficher la fenêtre. On pourra utiliser par exemple les constantes SW_SHOW, SW_HIDE, SW_MINIMIZE, SZ_MAXIMIZE, etc. D'habitude, on lui passe le paramètre nCmdShow de WinMain afin que la fenêtre s'affiche tout comme l'utilisateur l'a demandé.
II-D. Intercepter les messages▲
MSG msg;
while
(
GetMessage
(&
msg, NULL
, 0
, 0
))
{
TranslateMessage
(&
msg);
DispatchMessage
(&
msg);
}
La fonction GetMessage retourne TRUE tant qu'elle n'a pas reçu le message WM_QUIT. Une application doit donc envoyer ce message pour quitter la boucle. Une fois qu'on a quitté la boucle, on termine le programme.
II-E. Ecrire la procédure de fenêtre▲
LRESULT CALLBACK WndProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch
(
message)
{
case
WM_DESTROY:
PostQuitMessage
(
0
);
break
;
default
:
return
DefWindowProc
(
hwnd, message, wParam, lParam);
}
return
0L
;
}
Le message WM_DESTROY est envoyé par Windows lorsque la fenêtre est sur le point d'être détruite (après que l'utilisateur l'a fermée par exemple). A ce moment, nous devons alors poster le message WM_QUIT. C'est ce qu'on a fait avec la fonction PostQuitMessage. Le 0 passé en argument de cette fonction sera placé dans le paramètre wParam du message. C'est en quelque sorte un entier qui indique la raison pour laquelle on a posté le message. 0 indique une fin normale. Et enfin, il ne faut jamais ignorer un message. Si le message ne nous intéresse pas, laissons à Windows le soin de s'en occuper, en appelant tout simplement DefWindowProc.
II-F. Code complet▲
#include <windows.h>
LRESULT CALLBACK WndProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int
WINAPI WinMain
(
HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int
nCmdShow)
{
WNDCLASS wc;
HWND hWnd;
MSG msg;
wc.cbClsExtra =
0
;
wc.cbWndExtra =
0
;
wc.hbrBackground =
(
HBRUSH)(
COLOR_WINDOW +
1
);
wc.hCursor =
LoadCursor
(
NULL
, IDC_ARROW);
wc.hIcon =
LoadIcon
(
NULL
, IDI_APPLICATION);
wc.hInstance =
hInstance;
wc.lpfnWndProc =
WndProc;
wc.lpszClassName =
"
Classe 1
"
;
wc.lpszMenuName =
NULL
;
wc.style =
CS_HREDRAW |
CS_VREDRAW;
RegisterClass
(&
wc);
hWnd =
CreateWindow
(
"
Classe 1
"
,
"
Notre première fenêtre
"
,
WS_OVERLAPPEDWINDOW,
100
, 100
, 600
, 300
,
NULL
,
NULL
,
hInstance,
NULL
);
ShowWindow
(
hWnd, nCmdShow);
while
(
GetMessage
(&
msg, NULL
, 0
, 0
))
{
TranslateMessage
(&
msg);
DispatchMessage
(&
msg);
}
return
(
int
)msg.wParam;
}
LRESULT CALLBACK WndProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch
(
message)
{
case
WM_DESTROY:
PostQuitMessage
(
0
);
break
;
default
:
return
DefWindowProc
(
hwnd, message, wParam, lParam);
}
return
0L
;
}
II-G. Attributs d'une fenêtre▲
II-G-1. Définition▲
Les attributs d'une fenêtre son sa classe, son nom, son style, sa position, sa largeur et sa hauteur, sa fenêtre parent, l'instance de l'application à laquelle elle appartient ainsi que d'autres attributs que vous avez vous-même définis.
II-G-2. Nom d'une fenêtre▲
Le nom d'une fenêtre, appelé également texte de la fenêtre, est une chaîne de caractères qui sert à identifier la fenêtre pour l'utilisateur. Pour une fenêtre (ou feuille), ce nom apparaît dans la barre de titre (si elle en possède évidemment). La fonction GetWindowText permet de récupérer le texte d'une fenêtre et SetWindowText de le modifier.
BOOL SetWindowText
(
HWND hWnd, LPCTSTR lpString);
int
GetWindowText
(
HWND hWnd, LPTSTR lpString, int
nMaxCount);
On peut récupérer le handle d'une fenêtre connaissant sa classe et son nom avec la fonction :
HWND FindWindow
(
LPCTSTR lpClassName, LPCTSTR lpWindowName);
II-G-3. Style▲
Le style d'une fenêtre définit l'apparence et le comportement de la fenêtre. Nous avons déjà vu comment spécifier le style avec la fonction CreateWindow. La fonction CreateWindowEx permet de créer une fenêtre avec, en premier argument, un style étendu. Il s'utilise quasiment de la même manière que la fonction CreateWindow. Nous ne parlerons de ces styles que lorsque cela est vraiment nécessaire.
II-G-4. Position et dimensions▲
On peut déplacer et/ou redimensionner une fenêtre avec la fonction :
BOOL MoveWindow
(
HWND hWnd, int
x, int
y, int
nWidth, int
nHeight, BOOL bRepaint);
La fonction GetWindowRect permet de récupérer la position et les dimensions d'une fenêtre.
BOOL GetWindowRect
(
HWND hWnd, LPRECT lpRect);
Une structure de type RECT est composée de 4 champs de type LONG left, top, right et bottom
définissant le point supérieur gauche (left, top) et le point inférieur droit (right, bottom) d'un
rectangle.
Lorsqu'on crée une fenêtre, les dimensions passées à CreateWindow ou CreateWindowEx sont
celles de la fenêtre et non de la zone cliente (l'intérieur de la fenêtre). On peut connaître la
position de la zone cliente par rapport à son coin supérieur gauche (qui sera alors le point de
coordonnées (0, 0)) à l'aide de la fonction GetClientRect qui s'utilise de la même manière que
GetWindowRect. Pour ajuster un rectangle aux dimensions (et à la position) qu'il faut pour
obtenir un fenêtre qui aura une zone cliente qui correspond au rectangle spécifié au départ, on
pourra utiliser la fonction AdjustWindowRect (ou AdjustWindowRectEx si on veut spécifier
un style étendu).
BOOL AdjustWindowRect
(
LPRECT lpRect, DWORD dwStyle, BOOL bHasMenu);
Le code suivant permet d'obtenir une fenêtre avec une zone cliente contenue dans le rectangle (100, 100) - 320 x 200 par rapport à l'écran.
RECT rect;
LONG x =
100
, y =
200
, width =
320
, height =
200
;
/* On veut une zone cliente comme ceci : */
rect.left =
x;
rect.top =
y;
rect.right =
x +
(
width -
1
);
rect.bottom =
y +
(
height -
1
);
/* On ajuste le rectangle pour qu'il corresponde à celui de la fenêtre ... */
AdjustWindowRect
(&
rect, WS_BORDER |
WS_CAPTION |
WS_SYSMENU, FALSE);
/* ... et le tour est joué ! */
x =
rect.left;
y =
rect.top;
width =
(
rect.right -
rect.left) +
1
;
height =
(
rect.bottom -
rect.top) +
1
;
/* Il ne nous reste plus qu'à créer la fenêtre */
CreateWindow
(
"
Classe 1
"
,
"
Notre première fenêtre
"
,
WS_BORDER |
WS_CAPTION |
WS_SYSMENU,
x, y, width, height,
NULL
,
NULL
,
hInstance,
NULL
);
II-H. Le Z Order▲
Le Z order est une liste qui maintient la position des fenêtres le long d'un axe z orienté vers l'extérieur de l'écran. Ainsi, la fenêtre qui se trouve au sommet du Z order se trouve au premier plan et recouvre toutes les autres fenêtres. La fonction GetForegroundWindow retourne le handle de la fenêtre se trouvant au premier plan et SetForeground de spécifier une nouvelle fenêtre.
HWND GetForegroundWindow
(
void
);
BOOL SetForegroundWindow
(
HWND hWnd);
Le Z order est une file à priorité. Les plus prioritaires sont les fenêtres possédant le style étendu WS_EX_TOPMOST et apparaissent donc toujours en premier avant n'importe quelle autre fenêtre. Les fenêtres enfants sont groupées avec leur fenêtre parent. Lorsqu'on crée une fenêtre, Windows place cette fenêtre devant toutes les autres fenêtres de même priorité.