Fenêtres filles ou enfants
Des fenêtres filles ou enfants, pourquoi faire ?
Il est souvent pris en exemple l'écran du programme de dessin Paint de Microsoft:
Vous avez ici une fenêtre principale de Paint, et vous pouvez vite voir qu'il y a une zone dans cette fenêtre qui va vous permettre de dessiner. Cette zone peut dans certains cas avoir des ascenseurs, c'est le cas ici.
Il existe ensuite au moins deux autres zones dans cette fenêtre: la palette des outils et la palette de couleurs.
Et bien l'idée sera de mettre ces deux zones dans des fenêtres qui seront instanciées dans la fenêtre principale !
Chacunes de ces fenêtres aura ainsi sa propre procédure de gestion des messages, il sera donc possible de séparer les traitements de chacunes des fenêtres rendant la maintenance plus facile.
En gros, c'est le principe de la programmation (orienté) objet !
En réalité, il y a bien plus que ces trois fenêtres ! Les boutons d'une panel pourront devenir eux aussi des fenêtres, les ascenseurs, ... !
Pour diviser la fenêtre principale en différent zone, il faudra créer des fenêtres enfants qui viendront se poser sur la zone cliente de la fenêtre parent. La création de fenêtres enfants sera similaire à celle de la fenêtre principale, il faudra utiliser: CreateWindowEx(). Il suffit juste de spécifier le style WS_CHILD ainsi que le handle de la fenêtre parent pour que ces fenêtres deviennent enfants de la (future) fenêtre parent.
La différence notable par rapport à la fenêtre parent est le changement d'utilisation d'un paramètre de la fonction: celui ou vous indiquiez le menu de la fenêtre. Ce paramètre se transforme alors en identificateur de fenêtre fille.
Concernant la procédure de fenêtre:
Il est préférable de ne pas utiliser la même classe de fenêtre pour les filles que celle utilisée pour la fenêtre principale. En effet, ces fenêtres filles n'auront pas la même fonction que la fenêtre mère. Vous pourrez ainsi définir une procédure de gestions des messages spécifique à ces fenêtres (de même qu'un curseur ou une icône spécifiques), donc de séparer le code dans des fichiers totalement différents et indépendants. Vous aurez ainsi un développement et une maintenance plus facile et par conséquent un risque moins important d'erreurs.
En fait, Il s'agit d'un problème classique de la programmation objet: la hiérarchies de genre et savoir identifier la spécialisation de l'objet. Ici permettre d'effectuer un abstraction en utilisant le terme fenêtre, qui est imprécis au départ, mais que vous affinerez ensuite.
Voici un exemple tout bête de création de trois fenêtres filles avec pour chacunes d'elles, leur propre procédure de gestion des messages. Cet exemple va créer une fenêtre dans laquelle vous pourriez retrouver les outils du programme Paint, ainsi qu'une palette et enfin la zone ou l'utilisateur pourra dessiner.
Le dessin des trois fenêtres se fait lors du traitement WM_CREATE de la fenêtre principale. Cependant, les fenêtres ne seront pas encore affichées à ce stade. Je préfère en effet attendre le message WM_SIZE pour pouvoir positionner et tailler comme il faut ces trois fenêtres.
Un message pourra être intéressant:
WM_CHILDACTIVATE est envoyé à la procédure de gestion des messages des fenêtres si l'utilisateur clique sur la barre de titre de la fenêtre ou encore en déplaçant ou en modifiant la taille de la fenêtre.
Spécialisation des fenêtres
Dans la palette des outils, se trouve n outils ! Et bien pourquoi pas dessiner ces n outils dans n fenêtres. Vous obtenez donc de nouveau de fenêtres filles mais cette fois-ci de la fenêtre palette d'outils elle même fille de la fenêtre principale. L'ennui, c'est que pour l'utilisateur, ce n'est pas une fenêtre, mais un bouton. Et alors, rien ne vous empêche de dire qu'un bouton est une fenêtre spécialisée en bouton...
De plus, vous avez vu, dans le chapitre concernant la souris, qu'une fenêtre peut recevoir des évènements en provenance de la souris comme par exemple le clique ou le double cliques. Entre autre:
WM_LBUTTONDOWN est envoyé si le bouton gauche de la souris est pressé.
WM_LBUTTONUP est envoyé si le bouton gauche de la souris est relevé.
Avec ces messages, vous pouvez facilement dessiner un bouton pressé ou relaché en utilisant les fonctions GDI pour charger et afficher l'image correspondante ! C'est donc ici que vous allez pouvoir spécialiser les objets D'une fenêtre à la base, vous allons faire une spécialisation: un bouton !
Voici un exemple:
Le code:et
les images du bouton dans tous ses états :
Petits problèmes avec les fenêtres spécialisées
- Bien que l'utilisateur presse le bouton, l'application ne se termine pas. Il s'agit en fait de faire communiquer les fenêtres filles avec le parent. Ceci ne sera pas abordé ici, mais ce sera pour très bientôt (un raccourcie ici) !
- Autre petit problème, en fait, la fenêtre principale garde le contrôle sur pas mal de choses, ou plutôt n'informe pas ces fenêtres filles de tout ce qui se passe ! En effet, la fenêtre fille ne reçoit par exemple jamais les messages WM_SETFOCUS ou WM_KILLFOCUS, ceci même si l'utilisateur clique sur l'objet.
Voyez cet exemple:La
barre de titre de la fenêtre fille ne passe jamais en bleu et vous ne
verrez jamais les messages mis dans le code !
Il faudra alors utiliser la fonction FlashWindow(hwnd, true); pour forcer la barre de titre en bleu dans WM_CREATE par exemple...
Autres cas : Les fenêtres filles ne recevront jamais WM_DESTROY. Il faut impérativement que les enfants et les parents communiquent !
Pour revenir à l'exemple sur le bouton, si vous sortez trop rapidement de la zone client du bouton, la procédure de gestion des messages correspondantes ne détectera plus non plus la souris (Essayez donc ceci, cliquez sans relâcher sur l'image du bouton, l'image du bouton change (normal, vous appuyez sur le bouton), puis sans relâcher, sortez très rapidement de la zone client du bouton. Là vous pouvez relacher l'oreille de la petite bête et voyez: le bouton à l'écran semble figé sur position appuyée !
Heureusement, il y a des solutions. Par exemple pour la gestion de la souris, vous allez utiliser la fonction SetCapture() pour capturer la souris et savoir ainsi ce qui se passe:
On se limite à la capture de la souris que si l'utilisateur a cliqué sur le bouton, cette capture sera relâchée lorsque l'utilisateur aura lui même relâché son bouton dans ou hors zone client. Doù le nouvau code:
Voilà, vous pouvez voir le type de problèmes qui sera rencontré lors de l'utilisation de fenêtres filles...
Par contre, pour gérer des effets sur un simple clique en se basant uniquement sur un simple WM_xBUTTON, alors vous n'aurez auncun problème (genre case à cocher). Même principe avec l'application Paint, le choix d'une couleur dans la palette se fait lors du clique, relâché ou pas ensuite, le choix est immédiatement pris en compte.
Ce type de fenêtres répond aussi très bien à l'affichage de textes ou encore comme dans notre exemple, d'image.
Conclusion
Les fenêtres filles vont permettre de développer un objet spécialisé : le bouton, mais bien entendu, il existe déjà ce genre d'objets spécialisés dans Windows et qui eux fonctionnent très bien. Vous n'aurez donc pas souvent besoin de reprogrammer tout cela...
Ces fenêtres spécialisées sont les boutons, mais il en existe bien d'autres: les toggle-box, radio-set, ... étudiées dans un autre chapitre.
Enfin, une fenêtre fille peut connaître le handle de la fenêtre parent grace à la fonction GetParent(). Ceci étant vu plus tard dans le chapitre de gestion des messages (faire communiquer les fenêtres filles avec le parent).