Programmation Windows - Chap. 3 - Les fenêtres de dialogue
Partie 1/3


1. Introduction.

Nous revoila parti pour un nouveau tutorial sur la programmation Windows. Cette fois-ci nous allons aborder un sujet important et poursuivre notre exploration des multiples possibilités de gestion de fenêtres en parlant des Fenêtres de dialogue. Les fenêtres de dialogue, vous en voyez partout, elles contiennent des boutons, des icônes, des images, des choix mutliples, des champs de texte dynamiques, ... En voici un exemple rapide :



On y voit des champs de texte, des boutons, etc ... Elles permettent une itéraction directe avec l'utilisateur et de donner un aspect intuitif pour ce qui est de l'utilisation d'une application.

2. De quoi s'agit-t-il plus précisement ?.

Les boites de dialogues sont des fenêtres à part entière et répondent globalement aux mêmes règles. Elle doivent ainsi savoir comment réagir aux solicitations externes. Une particularité ,par contre, est qu'il faut les voir comme des structures fenètrées elle même composées de sous-structures apparentées à des fenêtres. En effet, chaque bouton, etc ... qui la compose est une entité qui est à même de recevoir, envoyer, etc ... des messages du types de ceux qu'on a vu dans le chapitre 2.
Une autre chose très importante est que les fenêtres de dialogue font parties de ce qu'on appel les ressources. Les ressources sont un ensemble ... de ressources :). Plus sérieusement elles sont un ensemble d'objets ( rien à voir avec la programmation objet attention ) d'un type autre que du code directement compilable mais qui ont un rapport directe avec notre programme puisqu'elles sont la pour être utilisées par lui. Elles peuvent être des images, des boites de dialogue, des fichiers sons, des fichiers textes, ... Pour être utilisables par notre programme de manière transparente pour l'utilisateur, ces ressources sont compilées par un compilateur spécifique et rajoutées à la fin de notre éxécutable une fois celui ci produit. De cette manière, toutes les données de ce type dont notre programme à besoin sont disponibles sans que l'utilisateur en ait conscience.
Il existe 2 manières, plus ou moins pratiques et rapides, de rajouter des ressources : une manière manuelle et une manière "automatisée". Nous ne verrons que la deuxieme puisque c'est la plus utilisée.

3.Ma premiere resource.

Puisqu'il faut bien commencer quelquepart nous allons créer notre premiere resource ensemble a l'aide de l'éditeur de resources de Visual C++. Pour cela, allez dans le menu Insert puis cliquez sur Resource (un Ctrl+R permet d'accelerer le processus). Nous avons alors acces a un menu nous permettant de choisir le type de resource que nous voulons ajouter. On retrouve ce dont je parlais plus haut a savoir les menus, les images, les dialogues, ... Choisissons une resource de type dialogue et faire New. L'éditeur de dialogue associé a VisualC++ est alors lancé directement avec une boite de dialogue de type standard contenant un bouton OK et un bouton Cancel. On pourrait presque continuer ici et prendre cette boite de dialogue comme premiere ébauche mais je vais m'apesantir sur un certain nombre de choses qui me semblent etre importantes.

3.1. Description de l'editeur de dialogue.

Dans la description que je vais faire ici, nous allons rencontrer un certain nombre de point importants qu'on retrouvera dans les autres types de resources. Tout d'abord voici le type de fenetre que vous devez obtenir :




L'editeur de Boite de Dialogue met donc a notre disposition un certain nombre d'outil :

  • Une vue directe de notre boite de dialogue,
  • Une barre d'outil specifique a la creation d'elements nouveau a cette boite de dialogue,
  • Une barre d'outil specifique servant notamment au formattage des elements constitutifs de notre boite de dialogue,
  • Une petite reglette nous permettant de positionner les elements de notre boite de dialogue,


  • Vous pouvez jouer un peu a ajouter des élements a la boite de dialogue pour constater les effets de vos actions et pour vous familiariser avec l'interface globale de creation de boite de dialogue.

    3.2. Les élements des boites de dialogue.

    Dans le vocabulaire specifique a Windows, un élement constitutif d'une boite de dialogue (bouton, champ de texte, ...) est appelé un controle. Ces controles sont ajoutés en general a la boite de dialogue a partir de l'editeur de VC++ mais ils peuvent aussi l'etre de maniere dynamique durant le runtime. Si vous essayez de cliquer sur un controle, il apparaitra une boite de dialogue permettant de configurer les caractéristiques du controle : son apparence, le texte qu'il contient, ... On y trouve notamment un champ tres tres important puisqu'il va nous permettre d'identifier et designer notre controle parmi les autres : son ID. Il est a noter que chaque boite de dialogue possede aussi une ID lui permettant de se distinguer des autres.



    Cette ID est juste un identifiant qui va servir a distinguer le controle.

    3.3. Creer et afficher une boite de dialogue.

    Nous allons creer une boite de dialogue et l'afficher a l'ecran. Pour cela, il nous faut ... une boite de dialogue :). Nous allons donc prendre, pour commencer, celle proposée par defaut par l'editeur de resource de VC++. Nous avons donc 2 boutons simple : un CANCEL et un OK. Ces boutons ont des ID specifiques, respectivement : IDCANCEL et IDOK. Maintenant que nous avons une boite de dialogue, nous allons l'ajouter au projet. Pour cela, sauvez le fichier de ressource (fichier ".rc" ascii simple contenant la description des ressources utilisees par l'application) avec un petit Ctrl^S. VC++ a normalement du generer un header specifique associe a notre fichier ressource et contenant tous les #defines specifiques aux ID de controle et permettant donc de les utiliser directement dans nos fichier sources. Il faut rajouter ces fichiers au projet, pour cela, faites un Project>Add to project>Files ... et selectionnez le fichier ".rc" et le header (resource.h). Et voila, nous sommes pres a utiliser notre boite de dialogue. Vous pouvez editer le fichier "resource.h" pour voir ce qu'il contient effectivement.

    Nous allons maintenant afficher notre boite de dialogue grace a la fonction DialogBox(). Voici un petit programme qui affiche une boite de dialogue, il ne contient rien de bien compliqué qui n'a pas deja ete explique :

    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <windowsx.h>
    
    HINSTANCE g_hInst;
    
    #include "resource.h"
    
    int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
    {
    	g_hInst = hInstance;
       	DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,NULL);
    	
    	return(0);
    }
    
    Premiere chose qu'on remarque c'est qu'il n'y a pas de boucle de recuperation des messages a mettre en place avec cette fonction. Cette fonction est en effet bloquante (c'est a dire en tres gros qu'elle va attendre qu'on lui demande explicitement de sortir grace a un message adéquat ) et dispose en interne de sa propre boucle de récupération et répartition des messages. En lancant ce programme, vous allez obtenir une boite de dialogue qui ne fait rien, rien du tout, et qui, en plus, ne peut pas etre quittée. La raison en est simple n'avons rien specifié quant aux actions a prendre lorsque l'utilisateur interagit avec la boite de dialogue. Juste une petite remarque concernant la macro MAKEINTRESOURCE() : dans un grand nombre de fonctions du SDK Win32, les ressources sont designées a travers une chaine de caracteres. Ainsi la fonction DialogBox prend en deuxieme parametre une variable de type LPCSTR, ce qui correspond (si on se rappel de la notation hongroise du chapitre 1) a un pointeur sur une chaine de caracteres, or l'editeur de VC++ ne nous permet de designer une resource que par une ID qui correspond au niveau code a un #define présent dans le header "resource.h" cree par VC++, il faut donc un outil nous permettant de faire une correspondance "bijective" :), entre une ID de type numérique et une chaine de caractere qui réfere directement au controle/a la resource. Cette correspondance est faite grace a la macro MAKEINTRESOURCE() qui prend un "parametre" de type entier et qui renvoi une chaine de caractere. Voila, voila.

    La maniere de spécifier a Windows la maniere de réagir dans le cas d'une action réalisée par l'utilisateur qui soit en relation avec notre boite de dialogue est du meme type que pour une fenetre toute simple en ce sens qu'elle fait intervenir une fonction de callback encore appelé "Dialog Procedure" (DlgProc). Une fois que cette fonction est correctement formatté, il nous suffit de passer un pointeur dessus grace au dernier parametre de la fonction DialogBox (que nous avons precedement mis a NULL). Le prototype de la fonction de callback doit etre le suivant :

    BOOL CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);

    La fonction recoit donc en parametre :

  • Un handle sur la boite de dialogue qui a envoye le message,
  • Le type de message recu,
  • Deux variables fourre-tout recevant les informations relatives au message (touche du clavier appuyée, ... ),

  • Voici un exemple minimaliste d'une DlgProc :

    BOOL CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
    {
    	switch(uMsg)
    	{
    		case WM_COMMAND:
    		{                                                       
    			switch(LOWORD(wParam))
    			{
    				case IDC_CANCEL: 
    				{
    					EndDialog(hwndDlg, 0);
    					return TRUE;
    					break;
    				}
    			
    				case IDC_OK:
    				{
    					EndDialog(hwndDlg,0);
    					return TRUE;
    					break;
    				}
    			}
    		}
    		default:
    			break;	
    	}	
    
    	return FALSE; 
    }
    
    Attention !!!! Il faut bien respecter les "return FALSE" et "return TRUE". Lorsqu'un message est traité par la DialogProc, elle doit renvoyer TRUE et FALSE dans le cas contraire. De plus, vous pouvez remarquer que pour une boite de dialogue, il n'y a pas d'appel a la fonction DefWindowProc() comme pour une fenetre, ceci est du au fait que chaque boite de dialogue dispose automatiquement en interne de sa propre DlgProc par defaut pour une certain nombre de messages (du type WM_MOVE (dans le cas ou la dlg box bouge), ...). Vous pouvez facilement le constater en revenant a la version précedente de l'appel a la fonction DialogBox :

    DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,NULL);
    
    De plus, il va vous falloir modifier les proprieter de la boite de dialogue dans l'editeur de resource de VC++, pour y rajouter les caracteristiques MinimizeBox/MaximizeBox et Border->Resizing comme suit :



    Bien, recompilez le tout maintenant. Et lancez l'application. Vouspouvez constater que vous pouvez tres bien agrandir et mettre la fenetre en icone, ainsi qu'en changer la taille. Windows dispose donc d'un traitement par defaut pour ce type de commandes appliquée a une boite de dialogue.
    Bon maintenant revenons a notre exemple de DlgProc ci-dessus. Nous y utilisons une nouvelle fonction : EndDialog(). Cette fonction sert tout simplement a fermer une boite de dialogue, mais juste a fermer une boite de dialogue pas a mettre un terme a l'application !!! Ca c'est toujours votre probleme. On indique a la fonction le handle de la boite de dialogue ainsi que la valeur de retour que doit renvoyer la fonction dialogBox(). En effet, cette fonction EndDialog() va mettre fin a la boucle interne de la fonction DialogBox() ( qui etait je vous le rappel bloquante ... et bloquée d'ailleurs ) et fournir une valeur numerique que devra renvoyer DialogBox(). Il nous est donc possible de la recuperer et de tester la maniere dont la boite de dialogue a ete terminee par exemple. Tiens, juste un apparté pour vous faire remarquer que ce comportement est le meme que pour la fonction MessageBox() que nous avions vu dans le premier chapitre. Il s'agit en effet d'une fonction bloquante qui fait appel a une boite de dialogue et le choix de l'utilisateur est renvoye par la fonction MessageBox().
    Ensuite, nous pouvons constater que nous testons le type de message que nous recevons ( variable locale uMsg ). Nous cherchons des messages de type WM_COMMAND qui correspondent a des indication que des controles ont ete tripotés :)). Comme je l'ai dit plus haut nous allons alors utiliser la variable wParam pour verifier quel controle est concerne. L'ID du controle concerne est contenu dans les 16 bits de poids faible de la variable WParam ( qui fait 32 bits ). La macro LOWORD() nous permet d'avoir facilement acces a cette valeur. Prennez l'habitude de ce genre de passe-passe avec LOWORD() et son grand frere HIWORD() pour extirper des variables wParam et lParam les informations qu'elles contiennent sur l'evenement courant.

    Bon ben vous voila pres a tripoter un peu les controles et les boites de dialogues.

    3.4. Les controles.

    Maintenant que nous avons vu un peu la maniere dont nous pouvons afficher/appeler une boite de dialogue, nous allons regarder un peu quelques controle parmi les plus evident a apprehender.

  • Les boutons
  • Les boutons sont un des types de controle les plus courant et les plus facile a manipuler dans un premier temps. Nous avons deja vu comment savoir si l'utilisateur avait appuye sur un bouton. J'aimerais preciser deux petites choses et vous en saurez suffisament pour les utiliser. La premiere concerne le texte qui est affiche dans le controle et qui est cense informer l'utilisateur de la fonctionnalite du bouton, vous pouvez aisement le changer grace a la fonction SetWindowText(). Et oui, une boite de dialogue n'est qu'une fenetre spécialisée et il en va de meme pour ses controles. Voici le prototype de la fonction en question :

    BOOL SetWindowText(HWND hwnd, LPCTSTR lpString);

    Le premier argument est le handle de la fenetre dont on veut changer le texte. On peut recuperer ce handle grace a un autre fonction GetDlgItem() qui renvoi une valeur de type HWND si on lui donne le handle de la boite de dialogue ainsi que l'ID du controle en question. Voici un exemple d'utilisation de ces deux fonctions :

    SetWindowText( GetDlgItem(hMaDlg, IDOK), "Mon nouveau texte");

  • Les Edit Box
  • Une edit box est un controle contenant du texte pouvant etre modifie par l'utilisateur (dans la plupart du cas). En voici un exemple :





    Ce controle va pouvoir recevoir un texte tapé par l'utilisateur. Il nous faut donc une maniere de le récuperer, de le traiter etc ... Ce controle va me donner une occasion de revenir sur un aspect de Windows : les messages. Vouz devez commencer a savoir un peu a quoi ca correspond, nous avons vu qu'une boite de dialogue et plus generalement une fenetre recoit et emet des messages. Et bien, un controle lui aussi peut en recevoir et en emettre. c'est une chose tres importante a savoir puisque vous pouvez entierement prendre en main un controle grace a ces messages. Ils peuvent correspondre a des ordres divers du type inscrit ce texte dans tel endroit, ajoute telle chaine de caractere a tel endroit dans une liste, etc ... et correspondre a des informations provenant du controle diverses aussi du type l'utilisateur vient de selectionner tel objet dans la liste, l'utilisateur vient de taper un caractere dans la zone de texte de tel EditBox etc ...
    Pour pouvoir communiquer de tel messages a un controle il est bien evident qu'il faut pouvoir recevoir ces messages : ca on l'a deja vu grace a la DlgProc. Mais il faut aussi pouvoir en envoyer ca c'est nouveau. Et oui il est possible pour nous aussi d'envoyer des messages a l'intention d'une fenetre. Nous l'avions plus ou moins vu rapidement avec la fonction PostQuiMessage() dans le chapitre 2 sur les fenetres qui servait a envoyer un message WM_QUIT a notre fenetre pour que nous puissions le recuperer et quitter l'application. Bon et bien deux fonctions peuvent etre utilisees generalement pour envoyer des messages : PostMessage() et SendMessage(). La difference entre ces deux fonctions est simple : PostMessage() est non bloquante alors que SendMessage() est bloquante et attend que l'objet destinataire ait recu et traite le message pour retourner. Voici les prototypespour ces deux fonctions :

    LRESULT SendMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    LRESULT PostMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    Je crois que la je dois pouvoir me passer d'expliquer les differents parametres des deux fonctions vu qu'on les a deja rencontrés precedement. Revenons a nos moutons. Pour lire le message contenu dans une edit box il faut lui envoyer un message de type WM_GETTEXT en lui fournissant dans lParam un pointeur sur un buffer ou Windows va placer le texte contenu dans l'edit box et dans wParam le nombre de bytes maximum que le message peut avoir (= le nombre de bytes du tableau ). Voici un exemple d'utilisation :

    SendMessage(GetDlgItem(hMaDlg, ID_EDITBOX1), WM_GETTEXT, sizeof(szMonTableau), (LPARAM) szMonTableau);

    De meme, pour ecrire un texte dans un edit box il faut envoyer le message WM_SETTEXT avec 0 comme valeur pour wParam et un pointeur sur la chaine de caractere dans wParam. Une autre alternative a ces fonction d'envoi de messages sont les fonctions SetWindowText() et GetWindowText() que nous avons deja vu. Voila ! Il vous est maintenant possible de jouer avec les Edit Box.
    Ah oui, une derniere chose utile. Au meme titre qu'un message WM_CREATE est envoye lors de la phase de creation d'une fenetre, un message WM_INITDIALOG est envoye a une dialog box lorsque celle ci est entrain d'etre cree et juste avant qu'elle soit affichee. Vous pouvez donc l'utiliser pour initialiser vos controles etc... avant que la boite de dialogue ne soit visible. Voici un exemple :

    BOOL CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
    {
    	switch(uMsg)
    	{
    	case WM_INITDIALOG:
    		
    		SetWindowText(GetDlgItem(hwndDlg, IDOK), "Test");
    		return TRUE;
    		break;
    
    	case WM_COMMAND:
    	{                       
    		switch(LOWORD(wParam))
    		{
    			case IDC_CANCEL:
    			{
    				EndDialog(hwndDlg, 0);
    				return TRUE;
    				break;
    			}
    
    		case IDC_OK:
    		{
    			EndDialog(hwndDlg,0);
    			return TRUE;
    			break;
    			}
    		}
    	}
    	default:
    		break;
    	}
    	return FALSE;
    }
    
    Dans cet exemple, nous ecrivons le texte "Test" dans le bouton dont l'ID correspond a IDOK. Si ce bouton contenait un texte "OK", il contient maintenant un texte "Test" sans que l'utilisateur n'en voit rien.

    Voila pour cette premiere partie sur les boites de dialogue. La prochaine fois, dans la deuxieme partie, j'ai decidé de passer en revu un certain nombre de controles/trucs plus ou moins abordables qui pourront etre utile pour grandement ameliorer vos boites de dialogue, en vrac : les ToolTips, les ComboBox/ListBox, les SplitterBar, les barres de progression, les check box, les TreeView, les menus. Je donnerais les informations de maniere brute sans trop d'exemple. La raison en est simple, je prevois de faire une derniere partie sur les boites de dialogue uniquement concernant la programmation d'un petit utilitaire a nous mettant en pratique tous ces controles et toutes nos connaissances sur la programmation Windows. Ca vous permettra d'avoir un exemple de la maniere dont toutes ces choses peuvent marcher en pratique et de voir quelques nouvelles choses.
    En attendant, jouez avec l'exemple et matez un peu MSDN car c'est la qu'on apprend !
    N'hesitez pas a m'envoyer quelques emails pour me dire ce que vous pensez de ces tutoriaux pour me donner des idées de sujets ou pour n'importe quoi d'autre.
    A++.

    Deuxieme_Projet_Chap3.zip

    --
    Document ecrit par ABREU Alexandre : wiss1976@yahoo.fr
    Libre reproduction et diffusion autorisée - modifications interdites sans autorisation de l'auteur.

    Précédent Suivant