CheckBox, boutons, boutons radios, et zone de texte

CheckBox, boutons, boutons radios, et zone de texte

DialogBoxParam,SendMessage

Voir version :

Pas de dépendances

Télécharger :

Plusieurs fichiers :
04_12_02_dialog.c
04_12_02_resource.h
04_12_02_resource.rc


04_12_02_dialog.c

#include <stdio.h> #include <windows.h> #include <commctrl.h> #include "04_12_02_resource.h" #pragma comment(lib,"comctl32.lib") typedef struct { int check; int radio; TCHAR texte[500]; } Sdata; INT_PTR wmcommand(HWND hDlg, WPARAM wParam, LPARAM lParam,Sdata* sdat) { unsigned short id = LOWORD(wParam); switch(id) { case IDD_BOUTON1: { int nbc; LPWORD tmp = (LPWORD) sdat->texte; HWND hEdit = GetDlgItem(hDlg, IDD_EDIT1); *tmp = sizeof(sdat->texte); nbc = SendMessage(hEdit, EM_GETLINE, 0,(LPARAM)sdat->texte); EndDialog(hDlg, 1); } return TRUE; case IDD_RADIO1: sdat->radio = 1; return TRUE; case IDD_RADIO2: sdat->radio = 2; return TRUE; case IDD_CHECK1: { HWND hCombo = GetDlgItem(hDlg, IDD_CHECK1); int val = SendMessage(hCombo, BM_GETCHECK, 0, 0); sdat->check = val; } return TRUE; } return FALSE; } INT_PTR CALLBACK DlgFenetre(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static Sdata* sdat; switch(msg) { case WM_INITDIALOG: sdat = (Sdata*)lParam; return TRUE; case WM_COMMAND: return wmcommand(hDlg,wParam,lParam,sdat); case WM_CLOSE: EndDialog(hDlg, 0); return TRUE; } return FALSE; } int main() { int ret; Sdata data; ZeroMemory(&data,sizeof(Sdata)); InitCommonControls(); ret = DialogBoxParam(0, MAKEINTRESOURCE(IDD_FENETRE), 0, DlgFenetre,(LPARAM)&data); printf("ret = %d\n",ret); printf("radio = %d\n",data.radio); printf("checkbox = %d\n",data.check); printf("editbox = %S\n",data.texte); // %S si on est en unicode (sizeof(TCHAR)==2) et %s si on n'est pas en unicode (et sizeof(TCHAR) = 1) return 0; }


04_12_02_resource.h

#define IDD_FENETRE 100 #define IDD_BOUTON1 101 #define IDD_RADIO1 102 #define IDD_RADIO2 103 #define IDD_CHECK1 105 #define IDD_EDIT1 104


04_12_02_resource.rc

#include <windows.h> #include "04_12_02_resource.h" IDD_FENETRE DIALOG 100, 100, 350, 200 STYLE DS_MODALFRAME|WS_VISIBLE|WS_CAPTION|WS_SYSMENU CAPTION "Titre" FONT 8, "Helv" BEGIN LTEXT "Plouf", -1, 10, 10, 50, 40 PUSHBUTTON "Valider",IDD_BOUTON1,50,20,125,15 AUTORADIOBUTTON "radio1",IDD_RADIO1,50,40,50,20 AUTORADIOBUTTON "radio2",IDD_RADIO2,50,60,50,20 AUTOCHECKBOX "checkbox1",IDD_CHECK1,50,80,50,20 EDITTEXT IDD_EDIT1,50,100,250,20 END END


Commentaires

	Maintenant que vous maitrisez le chapitre 4.12.1 sur la fenêtre simple, passons aux choses sérieuses.

	Lancez le programme comme le premier : une fenêtre apparaît avec plusieurs choses dessus :

	- des boutons radio : si vous en choisissez un, automatiquement l'autre se déselectionne. 
	Tel Higlander, il ne peut en rester qu'un.	Vous pouvez mettre autant de boutons radios que vous voulez, 
	s'ils sont dans le même groupe, automatiquement, un seul sera allumé à la fois
	Notez qu'on pourra plus tard voir pour faire des sous groupes dans les fenêtres, ce qui permettra de faire plusieurs
	groupes de boutons radio.

	- une checkbox : vous la cochez ou la décochez.

	- une zone de texte.

	- un bouton valider.

LE FICHIER RESSOURCE
********************
	
	Nous allons faire vite ici : regardez le fichier ressource, il se comprend aisément.
	Les mots clés sont 

	PUSHBUTTON : pour le bouton
	AUTORADIOBUTTON : pour un bouton radio
	AUTOCHECKBOX  : pour une checkbox
	EDITTEXT : pour une zone d'édition de texte.

	Chaque contrôle a un identifiant : voir dans ressource.h, sauf le LTEXT. 
	Je n'en ai pas mis pour le LTEXT (j'ai mis -1) car je ne vais pas le piloter depuis l'extérieur.

	Le but des identifiants étant simplement de pouvoir discuter avec les contrôles depuis le programme.
	Mon LTEXT, lui, reste fixe, bien que j'aurais pu lui donner un identifiant, par exemple si j'avais voulu changer le texte
	en plein programme.

	Chaque contrôle a aussi ses coordonnées.

	Normalement, tout devrait être clair.

LE MAIN
*******

	Dans mon main, j'ai créé une structure Sdata pour récupérer mes données. Le programme proposera de cocher/remplir ce qu'on veut, 
	et dès qu'on quitte, il affiche le tout à l'aide de printf.

	Une petite différence par rapport au programme précédent, j'ai utilisé DialogBoxParam à la place de DialogBox
	Tout simplement parce que j'ai besoin de lui passer un paramètre : l'adresse de ma structure pour qu'il la remplisse.

	La doc de cette fonction :
	http://msdn.microsoft.com/en-us/library/windows/desktop/ms645465(v=vs.85).aspx

	me dit que le paramètre sera récupéré en tant que lparam lors de l'event WM_INITDIALOG

	En effet, la fonction callback me donnera l'event dans msg, et 2 paramètres dans wparam et lparam qui varient selon l'évênement.

	Je fais donc un case WM_INITDIALOG, dans lequel je copie mon pointeur dans une variable static.
	Il est d'usage de faire des variables static dans les CALLBACK, qui restent en place tant que la fenêtre est ouverte.

	Maintenant qu'on a géré cette initialisation, nous avons aussi dans cette fonction l'event WM_CLOSE, qui ferme la fenêtre en 
	mettant comme valeur de retour 0. Rappelez vous, le deuxième paramètre que je passe à EndDialog est la valeur de retour que je 
	souhaite récupérer depuis DialogBoxParam.

	Bien, maintenant, l'event important, c'est WM_COMMAND.
	Il y a un évênement de ce type quand je clique sur un contrôle.

	Comme vous pouvez le constater, pour éviter d'emboîter des swicth (je déteste ça), j'appelle une autre fonction : wmcommand
	Cette fonction est tout à fait normale (pas de CALLBACK).

	Avant de continuer, un petit topo sur lparam et wparam.

LPARAM et WPARAM
****************

	Si vous faites des "go to def" sur ces deux paramètres, vous voyez que :

	LPARAM et WPARAM sont des  :	__int3264

	C'est à dire que sous un programme 32 bits, ils font 32 bits chacun (4 octets), sous un programme 64 bits, ils font 64 bits chacun (8 octets).

	Donc on peut mettre un pointeur dans ces valeurs, ou bien un entier de 1, 2, 4 octets (je crois que seuls les pointeurs prendront vraiment les 8 octets)

	Donc pour simplifier, on peut mettre 4 octets, ou un pointeur.

	Or, 4 octets, ça peut aussi être 2 fois 2 octets.

	Ainsi, dans un int 32 bits, on peut finalement ranger 2 shorts 16 bits. (on parler de short de poids faible, et short de poids fort)
	Les évênements Windows ne s'en privent pas, mais fournissent des macros pour récupérer facilement ces données.

	Ainsi, LOWORD(wParam) veut dire "le short de poids faible". (de wParam ici, mais pareil pour lParam)
	HIWORD(wParam) veut dire "le short de poids fort"

Ma fonction wmcommand
---------------------
	
	Revenons à nos moutons.

	Quand un event WM_COMMAND est généré, l'ID du contrôle sur lequel on a appuyé est stocké dans le short de poids faible de wParam.
	D'ou :
	unsigned short id = LOWORD(wParam);
	
	vérifiez ici dans la partie Remarks, le message source étant "Control"
	http://msdn.microsoft.com/en-us/library/windows/desktop/ms647591(v=vs.85).aspx

	Je switch sur cette valeur pour savoir sur quoi j'ai appuyé.
	
	** Les boutons radio. Le plus simple :
	Si j'ai cliqué sur un bouton radio (case IDD_RADIO1 ou IDD_RADIO2), je ne vais même pas le "lire", 
	le simple fait d'avoir cliqué dessus le selectionne.
	Je mets donc à jour ma structure.

	** La checkbox.
	Cette fois, si on clique sur la checkbox, elle peut se cocher ou se décocher.
	Pour savoir quelle valeur elle a, je vais être obligé de la lire.

	Pour ça, je vais récupérer son HWND à partir de son ID, avec GetDlgItem,
	puis, je vais lui envoyer un message, et elle va me répondre.

	Grâce à SendMessage, je dis à qui j'envoie le message, j'envoie le type de mon message (ici BM_GETCHECK), ainsi que des paramètres 
	optionnels (des wParam et lParam). Dans mon cas, pas besoin de paramètres, car ma requête est simple : BM_GETCHECK,
	es tu cochée ou non ?
	http://msdn.microsoft.com/en-us/library/windows/desktop/bb775986(v=vs.85).aspx

	Je récupère le résultat et je le met dans ma structure.

	** L'edit box.
	Comme vous le voyez, je n'ai pas fait de case sur IDD_EDIT1. Pourquoi ?
	--> Parce que si on clique sur la zone de texte, je ne vais rien faire de particulier. On pourrait très bien faire quelque chose.

	Un exemple : avez vous déjà posté un message sur Jeuxvideo.com ? 
	Quand vous le faites, la zone de texte est déjà remplie, et contient "Ne postez pas d'insultes, blablabla"
	Dès que vous cliquez dessus, ça s'efface, pour que puissiez troll... euh, poster un message.

	C'est ce que j'aurais pu faire, par exemple, si j'avais mis un case IDD_EDIT1 sous msg = WM_COMMAND, j'aurais pu effacer le texte existant

	Du coup, l'edit box, vous la remplissez comme vous voulez, on va voir juste maintenant comment on récupère les données.

	** le bouton Valider.
	Si on appuie sur le bouton, je fais un EndDialog(hDlg, 1);
	Notez qu'ici, je mets 1 en code de sortie, pour différencier avec la croix qui me met 0. D'ailleurs, le printf marque la valeur de ret.

	Mais avant de faire le EndDialog, il s'agit de copier les données de la editbox dans le champ texte que j'ai mis dans ma structure.

	Pour cela, la démarche a quelque chose d'exotique.

	Tout d'abord, connaissez vous TCHAR ? C'est un type Windows qui est soit un char, soit un wchar_t en fonction du fait que vous soyez 
	en unicode ou pas. Par défaut, vous l'êtes, voila pourquoi dans le printf, j'ai mis %S et non %s. Changez si vous n'êtes pas en unicode.

	Donc l'idée va être de récupére le HWND de l'éditbox (hEdit), puis de lui envoyer un message.
	Ce message, ça va être EM_GETLINE
	http://msdn.microsoft.com/en-us/library/windows/desktop/bb761584(v=vs.85).aspx

	Ce message attend 2 paramètres.
	Tout d'abord, dans wParam, le numéro de la ligne voulue. Car une editbox pourrait recevoir plusieurs lignes (ici, je n'en ai permis qu'une)
	Donc je mets 0 dans wParam.

	Pour lParam, il faut que je donne le pointeur vers le tableau ou j'attends ma réponse : donc sdat->texte.
	Mais il y a un soucis : pour éviter de déborder, il est important que j'envoie AUSSI la taille de mon tableau. 
	Ainsi, la copie de la chaine se fera, mais si elle est trop longue, elle sera tronquée, et ne débordera pas.

	Mais ou mettre cette donnée, puisque lParam et wParam sont déjà occupés ? 
	Et bien, comme dit la doc, dans le tableau de sortie ! Et en castant violemment !

	Typiquement, j'ai un tableau de char, mais je vais coller un int dedans (qui va donc prendre 4 octets, donc les 4 premiers éléments 
	du tableau).

	Moi c'est ce que j'appelle camoufler un paramètre. Mais ainsi, le contrôle le récupère, puis remplit ma chaine de façon sécurisée.
	Notez que la chaine écrase cette valeur dans le tableau de sortie : mon tableau de sortie est donc directement la chaine voulue.

	Voila, tout ceci explique tout cela !