II. Les boîtes de dialogue▲
II-A. Introduction▲
Une boîte de dialogue est une fenêtre, généralement temporaire, qui contient normalement un ou plusieurs contrôles permettant à l'utilisateur d'entrer des informations pour le programme. Cependant, au niveau de la programmation, les boîtes de dialogue ne s'utilisent pas tout à fait de la même façon que les fenêtres « normales » (bien qu'elles soient également des fenêtres) en raison justement de leur caractère temporaire (mais bien entendu, rien ne vous empêche d'avoir une boîte de dialogue comme fenêtre principale).
II-B. Deux types de boîtes de dialogue▲
Une boîte de dialogue peut être créée avec DialogBox ou CreateDialog (qui sont en fait des macros qui utilisent CreateWindowEx en passant bien sûr les bons paramètres). La première crée une boîte de dialogue modale et la seconde une boîte de dialogue non modale. Une boîte de dialogue modale suspend le travail de sa fenêtre parent jusqu'à ce que l'utilisateur en a terminé avec. Une boîte de dialogue non modale est une fenêtre qui attend des informations venant de l'utilisateur sans suspendre le travail des autres fenêtres. Je ne vous apprends donc rien, puisque vous l'avez certainement deviné, en affirmant que DialogBox attend que l'utilisateur en ait fini avec la fenêtre (la boîte de dialogue) avant de retourner tandis que CreateDialog retourne immédiatement après l'appel.
Il faut également savoir qu'une boîte de dialogue modale sera toujours affichée par Windows que le style WS_VISIBLE ait été spécifié ou non. Par contre Windows n'affiche pas automatiquement une boîte de dialogue non modale (il faut donc avoir spécifié ou alors appeler la fonction ShowWindow).
La plupart du temps, une boîte de dialogue possède une fenêtre parent. Sauf dans le cas où la elle est utilisée comme fenêtre principale de l'application, il est en fortement conseillé de toujours spécifier une fenêtre parent lors de sa création. En effet, lorsqu'on ne spécifie aucun parent, la boîte de dialogue devient complètement autonome ce qui va évidemment compliquer sa gestion.
II-C. Traitement▲
La boîte de dialogue doit tout d'abord être décrite (par exemple en ressource) puis créée à l'aide de DialogBox ou CreateDialog.
INT_PTR DialogBox
(
HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWnd, DLGPROC lpDialogProc);
Où lpTemplate est le nom de la boîte de dialogue qu'on veut afficher et lpDialogProc l'adresse d'une fonction qui sera la procédure de traitement des messages.
INT_PTR CALLBACK DialogProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
Cette procédure doit retourner TRUE si le message a été traité, FALSE dans le cas contraire (sachez également que, dans les anciennes versions du SDK Windows, cette fonction ne retournait pas un INT_PTR, mais BOOL). Donc si FALSE est retourné, Windows va effectuer le traitement par défaut. On n'a donc plus besoin de DefWindowProc. Et enfin, sachez qu'avec les boîtes de dialogue, le message WM_CREATE est remplacé par le message WM_INITDIALOG. Si l'on souhaite passer des paramètres à travers ce message, On utilisera DialogBoxParam ou CreateDialogParam à la place de DialogBox ou CreateDialog.
INT_PTR DialogBoxParam
(
HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWnd,
DLGPROC lpDialogProc, LPARAM dwInitParam );
II-D. Création▲
Nous allons maintenant voir comment créer un modèle (ou template) de boîte de dialogue (autrement dit : une boîte de dialogue !) en ressource. Là encore, comme vous pouvez le constater, les éditeurs de ressources sont plus que jamais d'une aide précieuse, mais ici nous allons quand même taper le code nous-mêmes.
#include <windows.h>
MyDialog DIALOGEX 0
, 0
, 100
, 50
STYLE DS_CENTER |
WS_POPUP |
WS_CAPTION |
WS_SYSMENU
CAPTION "
Ma boîte de dialogue
"
{
CTEXT "
Hello, world !
"
, -
1
, 0
, 8
, 100
, 12
DEFPUSHBUTTON "
OK
"
, IDOK, 25
, 24
, 50
, 14
}
DIALOGEX est une amélioration de DIALOG (que pouvez toutefois continuer à utiliser, mais cela est-il justifié ?)
_name DIALOGEX _x, _y, _width, _height
STYLE _style
CAPTION _caption
FONT _pointsize, _facename, _weight, _italic, _charset
MENU _idmenu
{
_content
}
Pour les contrôles, la syntaxe est :
<
TYPE DU CONTROLE>
_text, _id, _x, _y, _width, _height, _style, _exstyle
Où le type du contrôle peut être LTEXT, RTEXT, CTEXT (contrôles Static), PUSHBUTTON, DEFPUSHBUTTON, RADIOBUTTON, AUTORADIOBUTTON, CHECKBOX, AUTOCHECKBOX, STATE3, AUTO3STATE, PUSHBOX, GROUPBOX, EDITTEXT, LISTBOX, COMBOXBOX ou SCROLLBAR. Bien entendu, ces contrôles s'utilisent de la même façon que les contrôles créés dynamiquement.
On peut également créer un contrôle avec le mot-clé générique CONTROL :
CONTROL _text, _id, _classname, _x, _y, _width, _height, _style, _exstyle
Attention ! L'unité utilisée dans les boîtes de dialogue n'est pas le pixel, mais les Dialog Template Units. Ce ne sont pas des unités fixes. L'intérêt d'utiliser de telles unités est de pouvoir créer des boîtes de dialogue ayant la même proportion indépendamment de la résolution de l'écran. Alors juste pour info, on appelle Dialog Base Units d'une boîte de dialogue la largeur et la hauteur moyennes des caractères dans la police utilisée. Si la boîte de dialogue utilise la police système, on peut récupérer leurs équivalents en pixels à l'aide de la fonction GetDialogBaseUnits. Sinon, un autre moyen très simple de les récupérer est d'utiliser la fonction GetTextEntentPoint32 sachant qu'on appelle largeur moyenne des caractères dans une police donnée la largeur du caractère x. Si on connaît les Dialog Base Units, on peut directement convertir les Dialog Template Units en pixels sachant que :
1
XDialogTemplateUnit =
1
/
4
(
XDialogBaseUnit)
1
YDialogTemplateUnit =
1
/
8
(
YDialogBaseUnit)
En réalité, pour convertir les Dialog Template Units en pixels, on utilisera tout simplement la fonction MapDialogRect.
II-E. Une boîte de dialogue comme fenêtre principale▲
Nous allons enfin voir un exemple qui affiche une boîte de dialogue comme fenêtre principale. Voici donc le code :
include <
windows.h>
INT_PTR CALLBACK DialogProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int
WINAPI WinMain
(
HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int
nCmdShow)
{
DialogBox
(
hInstance, "
MyDialog
"
, NULL
, DialogProc);
return
0
;
}
INT_PTR CALLBACK DialogProc
(
HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch
(
message)
{
case
WM_INITDIALOG:
break
;
case
WM_COMMAND:
switch
(
LOWORD
(
wParam))
{
case
IDOK:
SendMessage
(
hwnd, WM_CLOSE, 0
, 0
);
break
;
}
break
;
case
WM_CLOSE:
EndDialog
(
hwnd, 0
);
break
;
default
:
return
FALSE;
}
return
TRUE;
}
La fonction EndDialog permet de détruire une boîte de dialogue modale. La valeur passée en dernier paramètre sera utilisée par DialogBox comme valeur de retour. Dans le cas d'une boîte de dialogue non modale, on utilise plutôt la fonction DestroyWindow (en fait, EndDialog utilise en interne DestroyWindow, mais après avoir récupéré la valeur que DialogBox doit retourner).
II-F. Les boîtes de dialogue communes▲
Dans les paragraphes précédents, nous avons appris à utiliser des boîtes de dialogue que nous avons nous-mêmes créées. Ici ce qui nous intéressera ce sont les boîtes de dialogue communes, c'est-à-dire celles qui sont fournies par Windows. Il s'agit des boîtes de dialogue Ouvrir, Enregistrer (Enregistrer sous), Imprimer, etc. En particulier, sachez que les boîtes de dialogue Ouvrir et Enregistrer sous sont en fait les mêmes (ou presque) ! Pour afficher la boîte de dialogue (Ouvrir ou Enregistrer sous), on initialise une structure de type OPENFILENAME qui décrit la boîte que l'on veut afficher ensuite on appelle GetOpenFileName pour afficher la boîte de dialogue Ouvrir et GetSaveFileName pour afficher la boîte de dialogue Enregistrer sous (ces boîtes de dialogue ont la même apparence, la seule véritable différence vient du texte du bouton par défaut qui est Ouvrir pour la boîte de dialogue Ouvrir et Enregistrer pour la boîte de dialogue Enregistrer sous), et c'est aussi simple que cela ! Par exemple :
OPENFILENAME ofn;
char
lpszFile[MAX_PATH] =
""
;
char
lpszFileTitle[MAX_PATH] =
""
;
ZeroMemory
(&
ofn, sizeof
(
ofn));
ofn.lStructSize =
sizeof
(
OPENFILENAME);
ofn.hwndOwner =
hwnd;
ofn.lpstrFilter =
"
Texte (*.txt)
\0
*.txt
\0
Tous (*.*)
\0
*.*
\0
"
;
ofn.lpstrFile =
lpszFile;
ofn.nMaxFile =
sizeof
(
lpszFile) /
sizeof
(
lpszFile[0
]);
ofn.lpstrFileTitle =
lpszFileTitle;
ofn.nMaxFileTitle =
sizeof
(
lpszFileTitle) /
sizeof
(
lpszFileTitle[0
]);
ofn.Flags =
OFN_EXPLORER |
OFN_HIDEREADONLY |
OFN_CREATEPROMPT;
if
(
GetOpenFileName
(&
ofn))
MessageBox
(
hwnd, lpszFile, lpszFileTitle, MB_OK);
else
MessageBox
(
hwnd, "
Opération annulée
"
, "
GetOpenFileName
"
, MB_OK);
Sachez cependant que OPENFILENAME est une structure particulièrement riche et que nous n'avons ici utilisé que quelques-uns de ses champs seulement. Elle est définie dans commdlg.h (inclus par windows.h) de la manière suivante :
typedef
struct
tagOFN {
DWORD lStructSize; /* [in] Taille de la structure */
HWND hwndOwner; /* [in] Fenêtre parent de la boîte de dialogue */
HINSTANCE hInstance; /* [in] Handle d'un module contenant une boîte de dialogue (1) */
LPCTSTR lpstrFilter; /* [in] Spécifie les filtres disponibles */
LPTSTR lpstrCustomFilter; /* [in/out] Contient le filtre préféré de l'utilisateur */
DWORD nMaxCustFilter; /* [in] Largeur du buffer lpstrCustomFilter */
DWORD nFilterIndex; /* [in/out] Contient l'index du filtre sélectionné (2) */
LPTSTR lpstrFile; /* [in/out] Contient le nom du fic. Doit être initialisé */
DWORD nMaxFile; /* [in] Largeur du buffer lpstrFile */
LPTSTR lpstrFileTitle; /* [in/out] Contient le nom et l'ext. seulement du fic. */
DWORD nMaxFileTitle; /* [in] Largeur du buffer lpstrFileTitle */
LPCTSTR lpstrInitialDir; /* [in] Pointeur vers le chemin du répertoire initial */
LPCTSTR lpstrTitle; /* [in] Titre (caption) de la boîte de dialogue */
DWORD Flags; /* [in/out] Style et comportement de la boîte de dialogue */
WORD nFileOffset; /* [out] Indice du 1er caractère du nom du fic. dans lpstrFile */
WORD nFileExtension; /* [out] Indice du 1er caractère de l'ext. du fic. dans lpstrFile */
LPCTSTR lpstrDefExt; /* [in] Ext. par défaut (3) */
LPARAM lCustData; /* [in] Paramètre à passer la lpfnHook via le msg WM_INITDIALOG */
LPOFNHOOKPROC lpfnHook; /* [in] DlgProc de la boîte de dialogue (4) */
LPCTSTR lpTemplateName; /* [in] Nom de la boîte de dialogue à charger */
#if (_WIN32_WINNT >= 0x0500)
void
*
pvReserved; /* [x] Reservé. Doit être NULL */
DWORD dwReserved; /* [x] Reservé. Doit être 0 */
DWORD FlagsEx; /* [in] Flags étendus */
#endif /* (_WIN32_WINNT >= 0x0500) */
}
OPENFILENAME, *
LPOPENFILENAME;
/* (1) : Requiert le flag OFN_ENABLETEMPLATEHANDLE. */
/* (2) : En entrée, 0 sélectionne le filtre préféré. */
/* (3) : Vous pouvez spéifiez une chaîne de 3 caractères tout au plus. Omettez le point. */
/* (4) : Requiert le flag OFN_ENABLEHOOK. */
Voici une liste des flags les plus courants.
OFN_ALLOWMULTISELECT : autorise la sélection multiple.
OFN_CREATEPROMPT : indique que l'utilisateur peut entrer un nom de fichier qui n'existe pas et dans ce cas il sera invité à confirmer son choix.
OFN_EXPLORER : impose le style Explorer. En fait ce style est appliqué par défaut, mais il est parfois annulé par certains flags. Ce flag permet de toujours utiliser le style Explorer.
OFN_FILEMUSTEXIST : utilisable uniquement dans une boîte de dialogue Ouvrir. Permet de spécifier que l'utilisateur doit entrer un nom de fichier existant. Inclut le flag OFN_PATHMUSTEXIST.
OFN_FORCESHOWHIDDEN : Windows 2000 et plus récents uniquement. Affiche les dossiers et fichiers cachés et/ou systèmes, indépendamment des préférences de l'utilisateur.
OFN_HIDEREADONLY : n'a de sens que dans une boîte de dialogue Ouvrir. Cache l'option « ouvrir en lecture seulement ».
OFN_NODEREFERENCELINKS : par défaut, si l'utilisateur sélectionne un raccourci (.lnk), le champ lpstrFile contient le chemin vers le fichier pointé par le lien. Le flag OFN_NODEREFERENCELINKS permet de spécifier que l'on désire dans un tel cas avoir le nom du lien et non celui du fichier pointé.
OFN_OVERWRITEPROMPT : dans une boîte de dialogue Enregistrer sous, provoque une demande de confirmation si l'utilisateur sélectionne un nom de fichier existant.
OFN_PATHMUSTEXIST : spécifie que l'utilisateur doit utiliser des caractères valides uniquement.
OFN_READONLY : n'a de sens que dans une boîte de dialogue Ouvrir. Coche l'option « ouvrir en lecture seulement ». Au retour de la fonction, ce flag indique l'état de ladite option.