Une remarque pertinente ?
Une critique impertinente ?
Un lynchage en règle ?
Une invitation sous les tropiques ?

Ecrivez-moi !
Conçu et enseigné tel qu'en lui même, avec pertes, fracas et humour de qualité supérieure            
 par Christophe Darmangeat dans le M2 PISE du Master MECI (Université Paris 7)            

 

 

 

Partie 4

Dialoguer avec l'utilisateur

Si vous êtes fins observateurs, vous avez sans doute remarqué une petite différence entre l'environnement de C# et celui des langages procéduraux traditionnels tels que le C. Finie en effet l'austère « console » de texte, avec son vilain curseur tout moche qui signalait à l'utilisateur que le programme attendait la saisie d'une valeur au clavier. Maintenant, on a des forms, des boutons, des trucs et des bidules à n'en savoir que faire, et en plus, on peut cliquer partout quand on veut. C'est quand même drôlement plus rigolo.

Dans les langages « à console », pour envoyer une information du programme vers l'utilisateur, on utilisait l'instruction Écrire ou son équivalent. Pour que le programme demande une valeur à l'utilisateur, on utilisait l'instruction Lire ou son équivalent. Et la vie suivait son cours. Les choses étaient peut-être monotones, mais au moins on savait à quoi s'en tenir.

Oui mais voilà, fini tout çà. Loin de se dérouler en suivant un fil linéaire, le dialogue (de sourds ?) entre l'homme et la machine va maintenant passer par de multiples canaux, qui sont autant de possibilités en plus de rendre la vie de l'homme plus joyeuse. Mais un peu plus compliquée, parfois, aussi.

1. La classe TextBox

1.1 Présentation

Dorénavant, donc, finie l'instruction Lire qui interrompait le déroulement du programme. La plupart, si ce n'est la totalité, des informations saisies par l'utilisateur le seront au travers d'objets créés à partir de classes spécialisées pour cela. Cela vous paraît obscur ? En fait, je ne fais que traduire en mots savants ce que vous savez tous par expérience ; car qui peut lire ces lignes sans avoir jamais saisi une information dans un programme fonctionnant sous Windows (ou MacOS, ou Android, c'est pareil) ?

Nous savons donc qu'il existe dans toute application informatique moderne des zones de l'écran dédiées pour que l'utilisateur puisse entrer du texte (ou des nombres). Cette zone dans le Framework .Net, c'est avant tout la « boîte à texte », ou TextBox. Il s'agit de la classe de prédilection pour permettre à l'utilisateur de passer des informations au programme — mais c'est loin d'être la seule, comme vous vous en doutez.

1.2 Propriétés

Il n'y a pas grand chose de particulier à dire de cette classe, qui possède la plupart des propriétés standard que nous avons déjà rencontrées. Toutefois, un point essentiel est que dans une TextBox, la propriété Text ne sert plus à désigner ce qui est écrit sur le contrôle (et donc fixé par le programmeur), mais ce qui se trouve à l'intérieur de la TextBox. Autrement dit, comme l'utilisateur a directement accès au contenu de la TextBox (sauf si on le lui interdit par une manoeuvre spéciale), il peut à loisir en modifier la propriété Text . Et c'est ainsi, au travers de cette propriété Text, qu'on récupère, dans le programme, ce qu'il aura saisi.

Une conséquence de ce qui précède est que tout ce que saisit l'utilisateur via une TextBox, même lorsqu'il s'agit de nombres, est traité par C# comme une chaîne de caractères, ce qui nous imposera parfois des conversions de types, autrement dit des « cast ».

Vite fait, bien fait, signalons quelques propriétés typiques de la classe TextBox :

  • Charactercasing : qui permet de convertir d'autorité le contenu en majuscules ou en minuscules
  • Multiline : qui autorise ou non la saisie de plusieurs lignes dans le contrôle
  • PasswordChar : qui permet de convertir d'office, à l'affichage uniquement, le texte tapé en un caractère (typiquement, l'astérique, pour des mots de passe)
  • ReadOnly : propriété booléenne qui interdira éventuellement à l'utilisateur de modifier le contenu de la TextBox.

1.3 Événements

Il est assez rare qu'on ait à gérer un événement sur un objet de la classe TextBox : la plupart du temps, on laisse l'utilisateur y écrire tranquillement ce qu'il souhaite, et ce n'est que dans un second temps, suite par exemple à un clic sur un bouton, que l'application en récupère le contenu. Si toutefois on avait besoin de faire réagir celle-ci en temps réel à toute modification survenue dans une Textbox (pour interdire la frappe de certains caractères, ou que sais-je encore), on dispose de l'événement TextChanged. On pourra ainsi programmer une procédure associée à cet événement, qui se déclenchera lors de toute modification de la propriété Text. Ainsi, si une telle procédure existe, taper « bonjour » dans une TextBox la déclenchera successivement sept fois (et même davantage si on a fait des fautes de frappe et des corrections !).

Tout ceci est plus que suffisant pour que vous vous dérouilliez les neurones avec quelques exercices :


Exercice
Exécutable
Sources
Calculator
Agglutinator

2. La classe « Boîte à Messages » (MessageBox)

2.1 Customiser et afficher une boîte

Du côté des possibles alternatives à l'instruction Écrire, nous avons déjà vu qu'il était possible de modifier par du code le texte associé à un contrôle (barre de titre de la Form, bouton, label, etc.). Dans bien des situations, c'est tout à fait satisfaisant. Mais on peut aussi souhaiter dégainer une arme bien connue de tout utilisateur de Windows : celle de la MessageBox, la « boîte à message », sorte de mini-form préfabriquée avec quelques options, telle que celle-ci :

MsgBox

Ces options des MessageBox, ce sont :

  1. le texte qui apparaît dans le corps de la boîte (ici : « Missile thermonucléaire... »)
  2. le texte qui apparaît dans le titre de la boîte (ici : « Dernière vérification »)
  3. le jeu de boutons proposé par la boîte ; cela peut aller du simple et unique « OK » à des palettes telles que celle de notre exemple (« Oui, Non, Annuler »).
  4. l'icône affichée sur la boîte (avec un choix entre les quatre possibilités bien connues des amateurs de Windows : information, interdiction, exclamation et, comme ci-dessus, interrogation)

Pour mettre en oeuvre tout cela, quelques informations sont bien utiles :

  • MessageBox est une classe.
  • Celle-ci est instanciée via la méthode Show – dans ce cas précis, instancier la classe MessageBox revient à faire apparaître une boîte à l'écran.
  • Cette méthode Show accepte quatre arguments correspondant, respectivement, aux quatre « options » listées ci-dessus.
  • Les deux premiers arguments sont de type string. Les deux derniers sont des énumérations. Kezako ?

2.2 Parenthèse indispensable : qu'est ce qu'une énumération ?

Une énumération est une association entre des mots-clés du langage et des nombres entiers. C'est donc, en quelque sorte, une table à deux colonnes, avec dans l'une des nombres entiers et dans l'autre des intitulés reconnus par le langage.

Une énumération peut avoir été créée par les concepteurs du langage eux-mêmes (c'est le cas ici) ; elle peut aussi être créée par le programmeur. Dans les deux cas, l'objectif est d'aider le programmeur, en lui épargnant d'avoir à entrer, donc à connaître, les codes en question. Une fois l'énumération créée, le programmeur disposera en effet d'une liste d'intitulés relativement explicites qu'il utilisera dans son code. Lors de la compilation, ces intitulés seront remplacés par les entiers auxquels ils sont associés dans l'énumération.

Dans le cas des MessageBox interviennent deux énumérations d'ores et déjà prévues par le langage :

  • MessageBoxButtons, qui contient la liste des jeux de boutons possibles. Cette énumération propose ainsi les valeurs Ok, OkCancel, RetryCancel, YesNo, YesNoCancel, AbortRetryCancel – est-il bien besoin de traduire ?
  • MessageBoxIcon, qui contient la liste des icônes disponibles. Celle-ci compte neuf valeurs différentes, mais qui se ramènent en réalité à seulement quatre icones. On ne retiendra donc que les valeurs Exclamation, Information, Question et Warning. Et là encore, on peut peut-être s'épargner la traduction...

Reprenons l'exemple affiché plus haut :

MsgBox

Pour l'afficher, on entrera donc le code :

string a = "Missile thermonucléaire armé. Voulez-vous continuer ?";
string b = "Dernière vérification";
MessageBox.Show(a, b, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information);

Ce n'est pas si compliqué, hein ? Allez, une dernière petite couche.

2.3 Récupérer le résultat d'une MessageBox

Quelle drôle d'idée ! Comment un bidule pareil pourrait-il produire un résultat qu'on puisse, ou doive, récupérer ? En fait, c'est très simple : si une MessageBox propose plus d'un bouton, c'est qu'elle donne un choix à l'utilisateur... et que ce choix a une certaine importance pour la suite du programme (ou alors, le programmeur est vraiment un petit plaisantin). En fait, le résultat d'une MessageBox, c'est le bouton sur lequel l'utilisateur clique pour la faire disparaître.

Pour récupérer convenablement ledit résultat, il suffit de savoir deux choses :

  • la méthode Show peut s'employer comme une procédure – c'est ainsi qu'on l'a utilisée dans l'exemple juste au-dessus. Mais elle peut aussi être utilisée comme une fonction : auquel cas, cette méthode renvoie un résultat (le bouton qui aura été cliqué).
  • les valeurs correspondant aux différents boutons sont les membres de l'énumération DialogResult : sans surprise, Ok, Cancel, Yes, No, Abort, Retry et Ignore.

Pour tester le résultat d'une MessageBox, on pourra donc stocker son résultat dans une variable de type DialogResult, avant d'interroger le contenu de cette variable :

string a ="Missile thermonucléaire armé. Voulez-vous continuer ?";
string b = "Dernière vérification";
DialogResult rep;
rep = MessageBox.Show(a, b, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (rep == DialogResult.Yes)
   {
    // il a dit oui
   }
else
   {
   // il a dit non
   }

Assez ri. Il est grand temps de vous dégourdir avec quelques petits exercices.
 

Exercice
Exécutable
Sources
Clicodrome
Choix cornélien
Convertisseur


3. Autres éléments d'interface

Vous le savez bien, entre l'utilisateur et les applications, un environnement graphique tel que Windows a mis en place moult autres voies de dialogue que le texte. Je pense en particulier aux cases, carrées ou rondes, dont il est toujours dommage qu'il en manque une. Tour d'horizon.

3.1 La classe Checkbox

La Checkbox, c'est cette charmante petite case carrée bien connue :

3.1.1 Propriétés

Passons rapidement sur les propriétés que les Checkbox partagent avec la plupart des autres classes, pour en venir à celle qui nous intéresse au premier chef : celle qui nous permet de savoir si une Checkbox est cochée ou non (si nous l'utilisons en lecture), ou de faire en sorte que Checkbox soit cochée ou non (si nous l'utilisons en écriture). Il s'agit de Checked : propriété booléenne, qui vaut naturellement True lorsque la case est cochée et False lorsqu'elle ne l'est pas.

Remarque anticipatrice (mais de peu) :
Les Checkbox sont souvent rassemblées dans un conteneur, qui les présente comme faisant partie d'un même groupe. Cette présentation est purement visuelle, pour ne pas dire décorative : elle n'exerce aucune espèce d'effet sur le fonctionnement de la Checkbox. Celle-ci est toujours individuelle et indépendante de ses éventuelles congénères.

3.1.2 Évènements

Le principal événement spécifique des CheckBox est CheckChanged. Il se déclenche chaque fois que la case est cochée ou décochée. On remarquera la différence subtile, mais réelle, entre les événements CheckChanged et Click, appliqués à une Checkbox :

  • Click ne se produira qu'en cas d'action sur la case via la souris (comme qui dirait, un clic).
  • CheckChanged, en revanche, se produira quelle que soit la raison qui aura provoqué le changement d'état de la case : un clic de souris, peut-être, mais aussi une ligne de code ou la frappe de la touche Espace.

Il faut donc bien réfléchir avant de choisir l'événement que l'on souhaite gérer, en sachant tout de même que dans l'immense majorité des cas, il s'agira logiquement de CheckChanged.

Remarque fine :
Il faut bien évidemment distinguer soigneusement le fait de récupérer à un moment donné l'état d'une case à cocher, ce qui se fait via la propriété Checked, dans n'importe quelle procédure, et le fait de vouloir que l'application réagisse à un changement d'état d'une case, ce qui se fait en créant la procédure associée CheckedChanged. Les deux problèmes — et les deux solutions — n'ont rien à voir l'un avec l'autre.
Cela va sans dire, mais ça va mieux en le disant.

On notera avec intérêt que lorsqu'une case Checkbox déclenche un événement CheckChanged, cela signifie que l'état de la case a changé. Mais rien n'indique donc si c'est parce qu'elle vient d'être cochée ou décochée... Si on a besoin de le savoir, on n'échappera pas à un test sur la propriété Checked.

3.1.3. Un raffinement suprême

Et maintenant, un petit tour de vice (non, ce n'est pas une faute d'orthographe).

Dans 99,99% des cas, ce qui précède suffira à notre bonheur. Oui, mais voilà, j'ai décidé de vous embêter et de pousser le contrôle dans ses retranchements. On peut en effet obtenir d'une CheckBox qu'elle fonctionne non sur deux, mais sur trois jambes, et qu'en plus des états « cochée » et « décochée », la case puisse aussi être « à moitié cochée » — cette situation est généralement censée indiquée que certains sous-élements correspondant à la Checkbox sont actifs, et d'autres non :

Pour pouvoir gérer cette situation nouvelle, il faut recourir à deux autres propriétés supplémentaires :

  • Threestate : propriété booléenne. Lorsqu'elle vaut True, elle permet à l'utilisateur d'avoir désormais le choix entre trois états et non plus deux. C'est donc cette propriété qui change le fonctionnement de la case.
  • Pour récupérer la valeur de la case (cochée, décochée ou à demi cochée), impossible désormais d'utiliser Checked, qui est une propriété booléenne. On utilisera alors Checkstate. Cette propriété, elle, n'est pas booléenne, et peut donc faire la différence entre les valeurs Checked, Unchecked et Indeterminate.
Remarque « utilisons notre science toute fraîche » :
Vous n'aurez bien sûr pas manqué d'en déduire que Checked, Unchecked et Indeterminate étaient des membres d'énumération, et donc qu'ils dissimulaient en réalité trois nombres entiers.

3.2 Les classes Radiobutton, GroupBox et Panel

Trois d'un coup, vous vous rendez compte ? Mais ce ne sont pas les plus pénibles. Voilà tout de suite une illustration de notre propos :

3.2.1 La question des conteneurs

Le RadioButton, dans sa forme classique, n'est donc autre que la célèbre petite case ronde. Par définition, cette case ne fonctionne qu'en groupe, conjointement avec d'autres, puisque par principe, une seule des cases d'un même groupe peut être cochée à la fois. Autrement dit, cocher une case d'un groupe décoche automatiquement les autres cases du groupe.

Ce groupe est défini par ce qu'on appelle un conteneur. Un conteneur est un objet qui en contient d'autres. Nous en connaissons déjà un : la Form, par définition, est le conteneur suprême de toute application. Les RadioButton, elles, seront regroupées au choix dans un GroupBox ou dans un Panel. La différence entre les deux n'est pas très grande, et surtout, elle est purement visuelle : le GroupBox est le conteneur standard, avec une bordure et un titre, comme dans l'exemple ci-dessus. Le Panel est un conteneur sans texte et sans bordure, donc invisible (mais néanmoins indispensable pour que les cases fonctionnent correctement en groupe).

Un RadioButton appartient donc au même groupe qu'un autre Radiobutton s'il est placé dans le même conteneur (généralement, un GroupBox). Ce simple fait suffit à faire marcher les Radiobutton les uns avec les autres. Il n'y a pas une seule ligne de code à écrire pour cela.

3.2.2 Propriétés

La propriété la plus utilisée du Radiobutton est Checked. C'est la même que pour les CheckBox : une propriété booléenne qui renvoie (ou définit) si un bouton radio est coché ou non.

Au passage, je signale une petite astuce pas chère qui peut éventuellement s'avérer utile dans certaines circonstances.  Les RadioButton peuvent en effet parfois prendre une drôle de tête, comme dans cet exemple :

Ici, nous avons un poste de radio (c'est de circonstance), où nous pouvons, via deux boutons, choisir une gamme de fréquences ou une autre. Sur l'image ci-dessus, c'est la FM qui est enclenchée.

Eh bien, ce look un peu inattendu est obtenu simplement à partir de RadioButton ! Mais leur propriété Appearance a pour valeur Button au lieu de Normal.

3.2.3 Evénements

Le changement d'état d'un bouton radio peut être provoqué par beaucoup de choses. Un clic, bien sûr. Mais aussi, la frappe de certaines touches du clavier. Donc, le même raisonnement que pour les CheckBox s'applique : si l'on doit gérer le changement d'état en temps réel, il faut utiliser l'événement CheckChanged.

Bon, si on se faisait trois petits exos ?

En ce qui concerne Options onéreuses, vous avez toutes les connaissances pour reproduire l'exécutable. Vos premières tenatives risquent fort d'aboutir à un code lourdingue et répétitif. Mais si vous utilisez judicieusement certaines connaissances mentionnées dans ce cours, vous pourrez l'alléger considérablement. En revanche, pour aboutir aux simplifications ultimes, il vous faut disposer de quelques outils supplémentaires... que nous découvrirons au chapitre suivant, pas avant.
Cette dernière remarque vaut de la même manière pour Sondage.

Exercice
Exécutable
Sources
Usine à messages
Options onéreuses
Sondage