Apprentissage du langage C#


précédentsommairesuivant

VIII. Interfaces graphiques avec C# et VS.NET

VIII-A. Les bases des interfaces graphiques

VIII-A-1. Un premier projet

Construisons un premier projet de type "Application windows" :

Image non disponible
  • [1] : créer un nouveau projet
  • [2] : de type Application Windows
  • [3] : le nom du projet importe peu pour le moment
  • [4] : le projet créé
Image non disponible
  • [5] : on sauvegarde la solution courante
  • [6] : nom du projet
  • [7] : dossier de la solution
  • [8] : nom de la solution
  • [9] : un dossier sera créé pour la solution [Chap5]. Les projets de celle-ci seront dans des sous-dossiers.
Image non disponible
  • [10] : le projet [01] dans la solution [Chap5] :
  • [Program.cs] est la classe principale du projet
  • [Form1.cs] est le fichier source qui va gérer le comportement de la fenêtre [11]
  • [Form1.Designer.cs] est le fichier source qui va encapsuler l'information sur les composants de la fenêtre [11]
  • [11] : le fichier [Form1.cs] en mode "conception" (design)
  • [12] : l'application générée peut être exécutée par (Ctrl-F5). La fenêtre [Form1] s'affiche. On peut la déplacer, la redimensionner et la fermer. On a donc les éléments de base d'une fenêtre graphique.

La classe principale [Program.cs] est la suivante :

 
CacherSélectionnez
  • ligne 2 : les applications avec formulaires utilisent l'espace de noms System.Windows.Forms.
  • ligne 4 : l'espace de noms initial a été renommé en Chap5.
  • ligne 10 : à l'exécution du projet (Ctrl-F5), la méthode [Main] est exécutée.
  • lignes 11-13 : la classe Application appartient à l'espace de noms System.Windows.Forms. Elle contient des méthodes statiques pour lancer / arrêter les applications graphiques windows.
  • ligne 11 : facultative - permet de donner différents styles visuels aux contrôles déposés sur un formulaire
  • ligne 12 : facultative - fixe le moteur de rendu des textes des contrôles : GDI+ (true), GDI (false)
  • ligne 13 : la seule ligne indispensable de la méthode [Main] : instancie la classe [Form1] qui est la classe du formulaire et lui demande de s'exécuter.

Le fichier source [Form1.cs] est le suivant :

 
CacherSélectionnez
  • ligne 5 : la classe Form1 dérive de la classe [System.Windows.Forms.Form] qui est la classe mère de toutes les fenêtres. Le mot clé partial indique que la classe est partielle et qu'elle peut être complétée par d'autres fichiers source. C'est le cas ici, où la classe Form1 est répartie dans deux fichiers :
  • [Form1.cs] : dans lequel on trouvera le comportement du formulaire, notamment ses gestionnaires d'événements
  • [Form1.Designer.cs] : dans lequel on trouvera les composants du formulaire et leurs propriétés. Ce fichier a la particularité d'être régénéré à chaque fois que l'utilisateur modifie la fenêtre en mode [conception].
  • lignes 6-8 : le constructeur de la classe Form1
  • ligne 7 : fait appel à la méthode InitializeComponent. On voit que cette méthode n'est pas présente dans [Form1.cs]. On la trouve dans [Form1.Designer.cs].

Le fichier source [Form1.Designer.cs] est le suivant :

 
CacherSélectionnez
  • ligne 2 : il s'agit toujours de la classe Form1. On notera qu'il n'est plus besoin de répéter qu'elle dérive de la classe Form.
  • lignes 25-37 : la méthode InitializeComponent appelée par le constructeur de la classe [Form1]. Cette méthode va créer et initialiser tous les composants du formulaire. Elle est régénérée à chaque changement de celui-ci en mode [conception]. Une section, appelée région, est créée pour la délimiter lignes 19-39. Le développeur ne doit pas ajouter de code dans cette région : il sera écrasé à la régénération suivante.

Il est plus simple dans un premier temps de ne pas s'intéresser au code de [Form1.Designer.cs]. Il est généré automatiquement et est la traduction en langage C# des choix que le développeur fait en mode [conception]. Prenons un premier exemple :

Image non disponible
  • [1] : sélectionner le mode [conception] en double-cliquant sur le fichier [Form1.cs]
  • [2] : cliquer droit sur le formulaire et choisir [Properties]
  • [3] : la fenêtre des propriétés de [Form1]
  • [4] : la propriété [Text] représente le titre de la fenêtre
  • [5] : le changement de la propriété [Text] est pris en compte en mode [conception] ainsi que dans le code source [Form1.Designer.cs] :
 
CacherSélectionnez

VIII-A-2. Un second projet

VIII-A-2-a. Le formulaire

Nous commençons un nouveau projet appelé 02. Pour cela nous suivons la procédure explicitée précédemment pour créer un projet. La fenêtre à créer est la suivante :

Image non disponible

Les composants du formulaire sont les suivants :

nom type rôle
1 labelSaisie Label un libellé
2 textBoxSaisie TextBox une zone de saisie
3 buttonAfficher Button pour afficher dans une boîte de dialogue le contenu de la zone de saisie textBoxSaisie

On pourra procéder comme suit pour construire cette fenêtre :

Image non disponible
  • [1] : cliquer droit sur le formulaire en-dehors de tout composant et choisir l'option [Properties]
  • [2] : la feuille de propriétés de la fenêtre apparaît dans le coin inférieur droit de Visual studio

Parmi les propriétés du formulaire à noter :

BackColor pour fixer la couleur de fond de la fenêtre
ForeColor pour fixer la couleur des dessins ou du texte sur la fenêtre
Menu pour associer un menu à la fenêtre
Text pour donner un titre à la fenêtre
FormBorderStyle pour fixer le type de fenêtre
Font pour fixer la police de caractères des écritures dans la fenêtre
Name pour fixer le nom de la fenêtre

Ici, nous fixons les propriétés Text et Name :

Text Saisies et boutons - 1
Name frmSaisiesBoutons
Image non disponible
  • [1] : choisir la boîte à outils [Common Controls] parmi les boîtes à outils proposées par Visual Studio
  • [2, 3, 4] : double-cliquer successivement sur les composants [Label], [Button] et [TextBox]
  • [5] : les trois composants sont sur le formulaire

Pour aligner et dimensionner correctement les composants, on peut utiliser les éléments de la barre d'outils :

Image non disponible
Image non disponible

Le principe du formatage est le suivant :

  • sélectionnez les différents composants à formater ensemble (touche Ctrl appuyée pendant les différents clics sélectionnant les composants)
  • sélectionnez le type de formatage désiré :

Une fois placés les composants nous fixons leurs propriétés. Pour cela, cliquer droit sur le composant et prendre l'option Properties :

Image non disponible
  • [1] : sélectionner le composant pour avoir sa fenêtre de propriétés. Dans celle-ci, modifier les propriétés suivantes : name : labelSaisie, text : Saisie
  • [2] : procéder de même : name : textBoxSaisie, text : ne rien mettre
  • [3] : name : buttonAfficher, text : Afficher
  • [4] : la fenêtre elle-même : name : frmSaisiesBoutons, text : Saisies et boutons - 1
  • [5] : exécuter (Ctrl-F5) le projet pour avoir un premier aperçu de la fenêtre en action.

Ce qui a été fait en mode [conception] a été traduit dans le code de [Form1.Designer.cs] :

 
CacherSélectionnez
  • lignes 53-55 : les trois composants ont donné naissance à trois champs privés de la classe [Form1]. On notera que les noms de ces champs sont les noms donnés aux composants en mode [conception]. C'est le cas également du formulaire ligne 2 qui est la classe elle-même.
  • lignes 7-9 : les trois objets de type [Label], [TextBox] et [Button] sont créés. C'est à travers eux que les composants visuels sont gérés.
  • lignes 14-19 : configuration du label labelSaisie
  • lignes 23-29 : configuration du bouton buttonAfficher
  • lignes 33-36 : configuration du champ de saisie textBoxSaisie
  • lignes 40-47 : configuration du formulaire frmSaisiesBoutons. On notera, lignes 43-45, la façon d'ajouter des composants au formulaire.

Ce code est compréhensible. Il est ainsi possible de construire des formulaires par code sans utiliser le mode [conception]. De nombreux exemples de ceci sont donnés dans la documentation MSDN de Visual Studio. Maîtriser ce code permet de créer des formulaires en cours d'exécution : par exemple, créer à la volée un formulaire permettant la mise à jour d'une table de base de données, la structure de cette table n'étant découverte qu'à l'exécution.

Il nous reste à écrire la procédure de gestion d'un clic sur le bouton Afficher. Sélectionner le bouton pour avoir accès à sa fenêtre de propriétés. Celle-ci a plusieurs onglets :

Image non disponible
  • [1] : liste des propriétés par ordre alphabétique
  • [2] : événements liés au contrôle

Les propriétés et événements d'un contrôle sont accessibles par catégories ou par ordre alphabétique :

  • [3] : Propriétés ou événements par catégorie
  • [4] : Propriétés ou événements par ordre alphabétique

L'onglet Events en mode Catégories pour le bouton buttonAfficher est le suivant :

Image non disponible
  • [1] : la colonne de gauche de la fenêtre liste les événements possibles sur le bouton. Un clic sur un bouton correspond à l'événement Click.
  • [2] : la colonne de droite contient le nom de la procédure appelée lorsque l'événement correspondant se produit.
  • [3] : si on double-clique sur la cellule de l'événement Click, on passe alors automatiquement dans la fenêtre de code pour écrire le gestionnaire de l'événement Click sur le bouton buttonAfficher :
 
CacherSélectionnez
  • lignes 10-12 : le squelette du gestionnaire de l'événement Click sur le bouton nommé buttonAfficher. On notera les points suivants :
  • la méthode est nommée selon le schéma nomDuComposant_NomEvénement
  • la méthode est privée. Elle reçoit deux paramètres :
  • sender : est l'objet qui a provoqué l'événement. Si la procédure est exécutée à la suite d'un clic sur le bouton buttonAfficher, sender sera égal à buttonAfficher. On peut imaginer que la procédure buttonAfficher_Click soit exécutée à partir d'une autre procédure. Celle-ci aurait alors tout loisir de mettre comme premier paramètre, l'objet sender de son choix.
  • EventArgs : un objet qui contient des informations sur l'événement. Pour un événement Click, il ne contient rien. Pour un événement ayant trait aux déplacements de la souris, on y trouvera les coordonnées (X,Y) de la souris.
  • nous n'utiliserons aucun de ces paramètres ici.

Ecrire un gestionnaire d'événement consiste à compléter le squelette de code précédent. Ici, nous voulons présenter une boîte de dialogue avec dedans, le contenu du champ textBoxSaisie s'il est non vide [1], un message d'erreur sinon [2] :

Image non disponible

Le code réalisant cela pourrait-être le suivant :

 
CacherSélectionnez

La classe MessageBox sert à afficher des messages dans une fenêtre. Nous avons utilisé ici la méthode Show suivante :

 
CacherSélectionnez

avec

text le message à afficher
caption le titre de la fenêtre
buttons les boutons présents dans la fenêtre
icon l'icone présente dans la fenêtre

Le paramètre buttons peut prendre ses valeurs parmi les constantes suivantes (préfixées par MessageBoxButtons comme montré ligne 7) ci-dessus :

constante boutons

AbortRetryIgnore

Image non disponible

OK

Image non disponible

OKCancel

Image non disponible

RetryCancel

Image non disponible

YesNo

Image non disponible

YesNoCancel

Image non disponible

Le paramètre icon peut prendre ses valeurs parmi les constantes suivantes (préfixées par MessageBoxIcon comme montré ligne 10) ci-dessus :


Asterisk

Image non disponible

Error

idem Stop

Exclamation

idem Warning

Hand

Image non disponible

Information

idem Asterisk

None

Image non disponible

Question

Image non disponible

Stop

idem Hand

Warning

Image non disponible
   

La méthode Show est une méthode statique qui rend un résultat de type [System.Windows.Forms.DialogResult] qui est une énumération :

Image non disponible

Pour savoir sur quel bouton a appuyé l'utilisateur pour fermer la fenêtre de type MessageBox on écrira :

 
CacherSélectionnez

VIII-A-2-b. Le code lié à la gestion des événements

Outre la fonction buttonAfficher_Click que nous avons écrite, Visual studio a généré dans la méthode InitializeComponents de [Form1.Designer.cs] qui crée et initialise les composants du formulaire, la ligne suivante :

 
CacherSélectionnez

Click est un événement de la classe Button [1, 2, 3] :

Image non disponible
  • [5] : la déclaration de l'événement [Control.Click] [4]. Ainsi on voit que l'événement Click n'est pas propre à la classe [Button]. Il appartient à la classe [Control], classe parente de la classe [Button].
  • EventHandler est un prototype (un modèle ) de méthode appelé delegate. Nous y allons y revenir.
  • event est un mot clé qui restreint les fonctionnalités du delegate EventHandler : un objet delegate a des fonctionnalités plus riches qu'un objet event.

Le delegate EventHandler est défini comme suit :

Image non disponible

Le delegate EventHandler désigne un modèle de méthode :

  • ayant pour 1er paramètre un type Object
  • ayant pour 2ième paramètre un type EventArgs
  • ne rendant aucun résultat

C'est le cas de la méthode de gestion du clic sur le bouton buttonAfficher qui a été générée par Visual Studio :

 
CacherSélectionnez

Ainsi la méthode buttonAfficher_Click correspond au prototype défini par le type EventHandler. Pour créer un objet de type EventHandler, on procède comme suit :

 
CacherSélectionnez

Puisque la méthode buttonAfficher_Click correspond au prototype défini par le type EventHandler, on pourra écrire :

 
CacherSélectionnez

Une variable de type delegate est en fait une liste de références sur des méthodes du type du delegate. Pour ajouter une nouvelle méthode M à la variable evtHandler ci-dessus, on utilisera la syntaxe :

 
CacherSélectionnez

La notation += peut être utilisée même si evtHandler est une liste vide.

Revenons à la ligne de [InitializeComponent] qui ajoute un gestionnaire d'événement à l'événement Click de l'objet buttonAfficher :

 
CacherSélectionnez

Cette instruction ajoute une méthode de type EventHandler à la liste des méthodes du champ buttonAfficher.Click. Ces méthodes seront appelées à chaque fois que l'événement Click sur le composant buttonAfficher sera détecté. Il n'y en a souvent qu'une. On l'appelle le "gestionnaire de l'événement".

Revenons sur la signature de EventHandler :

 
CacherSélectionnez

Le second paramètre du delegate est un objet de type EventArgs ou d'une classe dérivée. Le type EventArgs est très général et n'apporte en fait aucune information sur l'événement qui s'est produit. Pour un clic sur un bouton, c'est suffisant. Pour un déplacement de souris sur un formulaire, on aurait un événement MouseMove de la classe [Form] défini par :

 
CacherSélectionnez

Le delegate MouseEventHandler est défini comme :

Image non disponible

C'est une fonction déléguée (delegate) de signature void f (object, MouseEventArgs). La classe MouseEventArgs est elle définie par :

Image non disponible

La classe MouseEventArgs est plus riche que la classe EventArgs. On peut par exemple connaître les coordonnées de la souris X et Y au moment où se produit l'événement.

VIII-A-2-c. Conclusion

Des deux projets étudiés, nous pouvons conclure qu'une fois l'interface graphique construite avec Visual studio, le travail du développeur consiste principalement à écrire les gestionnaires des événements qu'il veut gérer pour cette interface graphique. Du code est généré automatiquement par Visual Studio. Ce code, qui peut être complexe, peut être ignoré en première approche. Ultérieurement, son étude peut permettre une meilleure compréhension de la création et de la gestion des formulaires.

VIII-B. Les composants de base

Nous présentons maintenant diverses applications mettant en jeu les composants les plus courants afin de découvrir les principales méthodes et propriétés de ceux-ci. Pour chaque application, nous présentons l'interface graphique et le code intéressant, principalement celui des gestionnaires d'événements.

VIII-B-1. Formulaire Form

Nous commençons par présenter le composant indispensable, le formulaire sur lequel on dépose des composants. Nous avons déjà présenté quelques-unes de ses propriétés de base. Nous nous attardons ici sur quelques événements importants d'un formulaire.

Load le formulaire est en cours de chargement
Closing le formulaire est en cours de fermeture
Closed le formulaire est fermé

L'événement Load se produit avant même que le formulaire ne soit affiché. L'événement Closing se produit lorsque le formulaire est en cours de fermeture. On peut encore arrêter cette fermeture par programmation.

Nous construisons un formulaire de nom Form1 sans composant :

Image non disponible
  • [1] : le formulaire
  • [2] : les trois événements traités

Le code de [Form1.cs] est le suivant :

 
CacherSélectionnez

Nous utilisons la fonction MessageBox pour être averti des différents événements.

ligne 10 : L'événement Load va se produire au démarrage de l'application avant même que le formulaire ne s'affiche : Image non disponible
   
ligne 15 : L'événement FormClosing va se produire lorsque l'utilisateur ferme la fenêtre. Image non disponible
ligne 19 : Nous lui demandons alors s'il veut vraiment quitter l'application : Image non disponible
ligne 20 : S'il répond Non, nous fixons la propriété Cancel de l'événement CancelEventArgs e que la méthode a reçu en paramètre. Si nous mettons cette propriété à False, la fermeture de la fenêtre est abandonnée, sinon elle se poursuit. L'événement FormClosed va alors se produire : Image non disponible

VIII-B-2. Etiquettes Label et boîtes de saisie TextBox

Nous avons déjà rencontré ces deux composants. Label est un composant texte et TextBox un composant champ de saisie. Leur propriété principale est Text qui désigne soit le contenu du champ de saisie soit le texte du libellé. Cette propriété est en lecture/écriture.

L'événement habituellement utilisé pour TextBox est TextChanged qui signale que l'utilisateur à modifié le champ de saisie. Voici un exemple qui utilise l'événement TextChanged pour suivre les évolutions d'un champ de saisie :

Image non disponible
type nom rôle
1 TextBox textBoxSaisie champ de saisie
2 Label labelControle affiche le texte de 1 en temps réel
AutoSize=False, Text=(rien)
3 Button buttonEffacer pour effacer les champs 1 et 2
4 Button buttonQuitter pour quitter l'application

Le code de cette application est le suivant :

 
CacherSélectionnez
  • ligne 24 : l'événement [Form].Shown a lieu lorsque le formulaire est affiché
  • ligne 26 : on met alors le focus (pour une saisie) sur le composant textBoxSaisie.
  • ligne 9 : l'événement [TextBox].TextChanged se produit à chaque fois que le contenu d'un composant TextBox change
  • ligne 11 : on recopie le contenu du composant [TextBox] dans le composant [Label]
  • ligne 14 : gère le clic sur le bouton [Effacer]
  • ligne 16 : on met la chaîne vide dans le composant [TextBox]
  • ligne 19 : gère le clic sur le bouton [Quitter]
  • ligne 21 : pour arrêter l'application en cours d'exécution. On se rappelle que l'objet Application sert à lancer l'application dans la méthode [Main] de [Form1.cs] :
 
CacherSélectionnez

L'exemple suivant utilise un TextBox multilignes :

Image non disponible

La liste des contrôles est la suivante :

type nom rôle
1 TextBox textBoxLignes champ de saisie multilignes
Multiline=true, ScrollBars=Both, AcceptReturn=True, AcceptTab=True
2 TextBox textBoxLigne champ de saisie monoligne
3 Button buttonAjouter Ajoute le contenu de 2 à 1

Pour qu'un TextBox devienne multilignes on positionne les propriétés suivantes du contrôle :

Multiline=true pour accepter plusieurs lignes de texte
ScrollBars=( None, Horizontal, Vertical, Both) pour demander à ce que le contrôle ait des barres de défilement (Horizontal, Vertical, Both) ou non (None)
AcceptReturn=(True, False) si égal à true, la touche Entrée fera passer à la ligne
AcceptTab=(True, False) si égal à true, la touche Tab générera une tabulation dans le texte

L'application permet de taper des lignes directement dans [1] ou d'en ajouter via [2] et [3].

Le code de l'application est le suivant :

 
CacherSélectionnez
  • ligne 18 : lorsque le formulaire est affiché (évt Shown), on met le focus sur le champ de saisie textBoxLigne
  • ligne 10 : gère le clic sur le bouton [Ajouter]
  • ligne 12 : le texte du champ de saisie textBoxLigne est ajouté au texte du champ de saisie textBoxLignes suivi d'un saut de ligne.
  • ligne 13 : le champ de saisie textBoxLigne est effacé

VIII-B-3. Listes déroulantes ComboBox

Nous créons le formulaire suivant :

Image non disponible
type nom rôle
1 ComboBox comboNombres contient des chaînes de caractères
DropDownStyle=DropDownList

Un composant ComboBox est une liste déroulante doublée d'une zone de saisie : l'utilisateur peut soit choisir un élément dans (2) soit taper du texte dans (1). Il existe trois sortes de ComboBox fixées par la propriété DropDownStyle :

Simple liste non déroulante avec zone d'édition
DropDown liste déroulante avec zone d'édition
DropDownList liste déroulante sans zone d'édition

Par défaut, le type d'un ComboBox est DropDown.

La classe ComboBox a un seul constructeur :

new ComboBox() crée un combo vide

Les éléments du ComboBox sont disponibles dans la propriété Items :

 
CacherSélectionnez

C'est une propriété indexée, Items[i] désignant l'élément i du Combo. Elle est en lecture seule.

Soit C un combo et C.Items sa liste d'éléments. On a les propriétés suivantes :

C.Items.Count nombre d'éléments du combo
C.Items[i] élément i du combo
C.Add(object o) ajoute l'objet o en dernier élément du combo
C.AddRange(object[] objets) ajoute un tableau d'objets en fin de combo
C.Insert(int i, object o) ajoute l'objet o en position i du combo
C.RemoveAt(int i) enlève l'élément i du combo
C.Remove(object o) enlève l'objet o du combo
C.Clear() supprime tous les éléments du combo
C.IndexOf(object o) rend la position i de l'objet o dans le combo
C.SelectedIndex index de l'élément sélectionné
C.SelectedItem élément sélectionné
C.SelectedItem.Text texte affiché de l'élément sélectionné
C.Text texte affiché de l'élément sélectionné

On peut s'étonner qu'un combo puisse contenir des objets alors que visuellement il affiche des chaînes de caractères. Si un ComboBox contient un objet obj, il affiche la chaîne obj.ToString(). On se rappelle que tout objet a une méthode ToString héritée de la classe object et qui rend une chaîne de caractères "représentative" de l'objet.

L'élément Item sélectionné dans le combo C est C.SelectedItem ou C.Items[C.SelectedIndex]C.SelectedIndex est le n° de l'élément sélectionné, ce n° partant de zéro pour le premier élément. Le texte sélectionné peut être obtenu de diverses façons : C.SelectedItem.Text, C.Text

Lors du choix d'un élément dans la liste déroulante se produit l'événement SelectedIndexChanged qui peut être alors utilisé pour être averti du changement de sélection dans le combo. Dans l'application suivante, nous utilisons cet événement pour afficher l'élément qui a été sélectionné dans la liste.

Image non disponible

Le code de l'application est le suivant :

 
CacherSélectionnez
  • ligne 5 : previousSelectedIndex mémorise le dernier index sélectionné dans le combo
  • ligne 10 : remplissage du combo avec un tableau de chaînes de caractères
  • ligne 12 : le 1er élément est sélectionné
  • ligne 15 : la méthode exécutée à chaque fois que l'utilisateur sélectionne un élément du combo. Contrairement à ce que pourrait laisser croire le nom de l'événement, celui-ci a lieu même si l'élément sélectionné est le même que le précédent.
  • ligne 16 : on note l'index de l'élément sélectionné
  • ligne 17 : s'il est différent du précédent
  • ligne 19 : on affiche le n° et le texte de l'élément sélectionné
  • ligne 21 : on note le nouvel index

VIII-B-4. Composant ListBox

On se propose de construire l'interface suivante :

Image non disponible

Les composants de cette fenêtre sont les suivants :

type nom rôle/propriétés
0 Form Form1 formulaire
FormBorderStyle=FixedSingle (cadre non redimensionable)
1 TextBox textBoxSaisie champ de saisie
2 Button buttonAjouter bouton permettant d'ajouter le contenu du champ de saisie [1] dans la liste [3]
3 ListBox listBox1 liste 1
SelectionMode=MultiExtended :
4 ListBox listBox2 liste 2
SelectionMode=MultiSimple :
5 Button button1vers2 transfère les éléments sélectionnés de liste 1 vers liste 2
6 Button button2vers1 fait l'inverse
7 Button buttonEffacer1 vide la liste 1
8 Button buttonEffacer2 vide la liste 2

Les composants ListBox ont un mode de sélection de leurs éléments qui est défini par leur propriété SelectionMode :

One un seul élément peut être sélectionné
MultiExtended multisélection possible : maintenir appuyée la touche SHIFT et cliquer sur un élément étend la sélection de l'élément précédemment sélectionné à l'élément courant.
MultiSimple multisélection possible : un élément est sélectionné / désélectionné par un clic de souris ou par appui sur la barre d'espace.
  • L'utilisateur tape du texte dans le champ 1. Il l'ajoute à la liste 1 avec le bouton Ajouter (2). Le champ de saisie (1) est alors vidé et l'utilisateur peut ajouter un nouvel élément.
  • Il peut transférer des éléments d'une liste à l'autre en sélectionnant l'élément à transférer dans l'une des listes et en choississant le bouton de transfert adéquat 5 ou 6. L'élément transféré est ajouté à la fin de la liste de destination et enlevé de la liste source.
  • Il peut double-cliquer sur un élément de la liste 1. Cet élément est alors transféré dans la boîte de saisie pour modification et enlevé de la liste 1.

Les boutons sont allumés ou éteints selon les règles suivantes :

  • le bouton Ajouter n'est allumé que s'il y a un texte non vide dans le champ de saisie
  • le bouton [5] de transfert de la liste 1 vers la liste 2 n'est allumé que s'il y a un élément sélectionné dans la liste 1
  • le bouton [6] de transfert de la liste 2 vers la liste 1 n'est allumé que s'il y a un élément sélectionné dans la liste 2
  • les boutons [7] et [8] d'effacement des listes 1 et 2 ne sont allumés que si la liste à effacer contient des éléments.

Dans les conditions précédentes, tous les boutons doivent être éteints lors du démarrage de l'application. C'est la propriété Enabled des boutons qu'il faut alors positionner à false. On peut le faire au moment de la conception ce qui aura pour effet de générer le code correspondant dans la méthode InitializeComponent ou le faire nous-mêmes dans le constructeur comme ci-dessous :

 
CacherSélectionnez

L'état du bouton Ajouter est contrôlé par le contenu du champ de saisie. C'est l'événement TextChanged qui nous permet de suivre les changements de ce contenu :

 
CacherSélectionnez
1.
2.
3.
4.
5.
        private void textBoxSaisie_TextChanged(object sender, System.EventArgs e) {
            // le contenu de textBoxSaisie a changé
            // le bouton Ajouter n'est allumé que si la saisie est non vide
            buttonAjouter.Enabled = textBoxSaisie.Text.Trim() != "";
        }

L'état des boutons de transfert dépend du fait qu'un élément a été sélectionné ou non dans la liste qu'ils contrôlent :

 
CacherSélectionnez

Le code associé au clic sur le bouton Ajouter est le suivant :

 
CacherSélectionnez

On notera la méthode Focus qui permet de mettre le "focus" sur un contrôle du formulaire. Le code associé au clic sur les boutons Effacer :

 
CacherSélectionnez

Le code de transfert des éléments sélectionnés d'une liste vers l'autre :

 
CacherSélectionnez

Les deux méthodes ci-dessus délèguent le transfert des éléments sélectionnés d'une liste à l'autre à une même méthode privée appelée transfert :

 
CacherSélectionnez
  • ligne b : la méthode transfert reçoit six paramètres :
  • une référence sur la liste contenant les éléments sélectionnés appelée ici l1. Lors de l'exécution de l'application, l1 est soit listBox1 soit listBox2. On voit des exemples d'appel, lignes 3 et 8 des procédures de transfert buttonXversY_Click.
  • une référence sur le bouton de transfert lié à la liste l1. Par exemple si l1 est listBox2, ce sera button2vers1( cf appel ligne 8)
  • une référence sur le bouton d'effacement de la liste l1. Par exemple si l1 est listBox1, ce sera buttonEffacer1( cf appel ligne 3)
  • les trois autres références sont analogues mais font référence à la liste l2.
  • ligne d : la collection [ListBox].SelectedIndices représente les indices des éléments sélectionnés dans le composant [ListBox]. C'est une collection :
  • [ListBox].SelectedIndices.Count est le nombre d'élément de cette collection
  • [ListBox].SelectedIndices[i] est l'élément n° i de cette collection

On parcourt la collection en sens inverse : on commence par la fin de la collection pour terminer par le début. Nous expliquerons pourquoi.

  • ligne f : indice d'un élément sélectionné de la liste l1
  • ligne h : cet élément est ajouté dans la liste l2
  • ligne j : et supprimé de la liste l1. Parce qu'il est supprimé, il n'est plus sélectionné. La collection l1.SelectedIndices de la ligne d va être recalculée. Elle va perdre l'élément qui vient d'être supprimé. Tous les éléments qui sont après celui-ci vont voir leur n° passer de n à n-1.
  • si la boucle de la ligne (d) est croissante et qu'elle vient de traiter l'élément n° 0, elle va ensuite traiter l'élément n° 1. Or l'élément qui portait le n° 1 avant la suppression de l'élément n° 0, va ensuite porter le n° 0. Il sera alors oublié par la boucle.
  • si la boucle de la ligne (d) est décroissante et qu'elle vient de traiter l'élément n° n, elle va ensuite traiter l'élément n° n-1. Après suppression de l'élément n° n, l'élément n° n-1 ne change pas de n°. Il est donc traité au tour de boucle suivant.
  • lignes m-n : l'état des boutons [Effacer] dépend de la présence ou non d'éléments dans les listes associées
  • ligne p : la liste l2 n'a plus d'éléments sélectionnés : on éteint son bouton de transfert.

VIII-B-5. Cases à cocher CheckBox, boutons radio ButtonRadio

Nous nous proposons d'écrire l'application suivante :

Image non disponible

Les composants de la fenêtre sont les suivants :

type nom rôle
1 GroupBox
cf [6]
groupBox1 un conteneur de composants. On peut y déposer d'autres composants.
Text=Boutons radio
2 RadioButton radioButton1
radioButton2
radioButton3
3 boutons radio - radioButton1 a la propriété Checked=True et la propriété Text=1 - radioButton2 a la propriété Text=2 - radioButton3 a la propriété Text=3
Des boutons radio présents dans un même conteneur, ici le GroupBox, sont exclusifs l'un de l'autre : seul l'un d'entre-eux est allumé.
3 GroupBox groupBox2  
4 CheckBox checkBox1
checkBox2
checkBox3
3 cases à cocher. chechBox1 a la propriété Checked=True et la propriété Text=A - chechBox2 a la propriété Text=B - chechBox3 a la propriété Text=C
5 ListBox listBoxValeurs une liste qui affiche les valeurs des boutons radio et des cases à cocher dès qu'un changement intervient.
6     montre où trouver le conteneur GroupBox

L'événement qui nous intéresse pour ces six contrôles est l'événement CheckChanged indiquant que l'état de la case à cocher ou du bouton radio a changé. Cet état est représenté dans les deux cas par la propriété booléenne Checked qui à vrai signifie que le contrôle est coché. Nous n'utiliserons ici qu'une seule méthode pour traiter les six événements CheckChanged, la méthode affiche. Pour faire en sorte que les six événements CheckChanged soient gérés par la même méthode affiche, on pourra procéder comme suit :

le composant radioButton1 et cliquons droit dessus pour avoir accès à ses propriétés :

Image non disponible

Dans l'onglet événements [1], on associe la méthode affiche [2] à l'événement CheckChanged. Cela signifie que l'on souhaite que le clic sur l'option A1 soit traitée par une méthode appelée affiche. Visual studio génère automatiquement la méthode affiche dans la fenêtre de code :

 
CacherSélectionnez

La méthode affiche est une méthode de type EventHandler.

Pour les cinq autres composants, on procède de même. Sélectionnons par exemple l'option CheckBox1 et ses événements [3]. En face de l'événement Click, on a une liste déroulante [4] dans laquelle sont présentes les méthodes existantes pouvant traiter cet événement. Ici on n'a que la méthode affiche. On la sélectionne. On répète ce processus pour tous les autres composants.

Dans la méthode InitializeComponent du code a été généré. La méthode affiche a été déclarée comme gestionnaire des six événements CheckedChanged de la façon suivante :

 
CacherSélectionnez

La méthode affiche est complétée comme suit :

 
CacherSélectionnez

La syntaxe

 
CacherSélectionnez

permet de vérifier que l'objet sender est de type CheckBox. Cela nous permet ensuite de faire un transtypage vers le type exact de sender. La méthode affiche écrit dans la liste listBoxValeurs le nom du composant à l'origine de l'événement et la valeur de sa propriété Checked. A l'exécution [7], on voit qu'un clic sur un bouton radio provoque deux événements CheckChanged : l'un sur l'ancien bouton coché qui passe à "non coché" et l'autre sur le nouveau bouton qui passe à "coché".

VIII-B-6. Variateurs ScrollBar

Il existe plusieurs types de variateur : le variateur horizontal (HScrollBar), le variateur vertical (VScrollBar), l'incrémenteur (NumericUpDown). Image non disponible Image non disponible Image non disponible

Réalisons l'application suivante :

Image non disponible
1234
type nom rôle
1 hScrollBar hScrollBar1 un variateur horizontal
2 hScrollBar hScrollBar2 un variateur horizontal qui suit les variations du variateur 1
3 Label labelValeurHS1 affiche la valeur du variateur horizontal
4 NumericUpDown numericUpDown2 permet de fixer la valeur du variateur 2
  • Un variateur ScrollBar permet à l'utilisateur de choisir une valeur dans une plage de valeurs entières symbolisée par la "bande" du variateur sur laquelle se déplace un curseur. La valeur du variateur est disponible dans sa propriété Value.
  • Pour un variateur horizontal, l'extrémité gauche représente la valeur minimale de la plage, l'extrémité droite la valeur maximale, le curseur la valeur actuelle choisie. Pour un variateur vertical, le minimum est représenté par l'extrémité haute, le maximum par l'extrémité basse. Ces valeurs sont représentées par les propriétés Minimum et Maximum et valent par défaut 0 et 100.
  • Un clic sur les extrémités du variateur fait varier la valeur d'un incrément (positif ou négatif) selon l'extrémité cliquée appelée SmallChange qui vaut par défaut 1.
  • Un clic de part et d'autre du curseur fait varier la valeur d'un incrément (positif ou négatif) selon l'extrémité cliquée appelée LargeChange qui vaut par défaut 10.
  • Lorsqu'on clique sur l'extrémité supérieure d'un variateur vertical, sa valeur diminue. Cela peut surprendre l'utilisateur moyen qui s'attend normalement à voir la valeur "monter". On règle ce problème en donnant une valeur négative aux propriétés SmallChange et LargeChange
  • Ces cinq propriétés (Value, Minimum, Maximum, SmallChange, LargeChange) sont accessibles en lecture et écriture.
  • L'événement principal du variateur est celui qui signale un changement de valeur : l'événement Scroll.

Un composant NumericUpDown est proche du variateur : il a lui aussi les propriétés Minimum, Maximum et Value, par défaut 0, 100, 0. Mais ici, la propriété Value est affichée dans une boîte de saisie faisant partie intégrante du contrôle. L'utilisateur peut lui même modifier cette valeur sauf si on a mis la propriété ReadOnly du contrôle à vrai. La valeur de l'incrément est fixée par la propriété Increment, par défaut 1. L'événement principal du composant NumericUpDown est celui qui signale un changement de valeur : l'événement ValueChanged

Le code de l'application est le suivant :

 
CacherSélectionnez

VIII-C. Événements souris

Lorsqu'on dessine dans un conteneur, il est important de connaître la position de la souris pour, par exemple, afficher un point lors d'un clic. Les déplacements de la souris provoquent des événements dans le conteneur dans lequel elle se déplace.

Image non disponible
  • [1] : les événements survenant lors d'un déplacement de la souris sur le formulaire ou sur un contrôle
  • [2] : les événements survenant lors d'un glisser / lâcher (Drag'nDrop)
MouseEnter la souris vient d'entrer dans le domaine du contrôle
MouseLeave la souris vient de quitter le domaine du contrôle
MouseMove la souris bouge dans le domaine du contrôle
MouseDown Pression sur le bouton gauche de la souris
MouseUp Relâchement du bouton gauche de la souris
DragDrop l'utilisateur lâche un objet sur le contrôle
DragEnter l'utilisateur entre dans le domaine du contrôle en tirant un objet
DragLeave l'utilisateur sort du domaine du contrôle en tirant un objet
DragOver l'utilisateur passe au-dessus domaine du contrôle en tirant un objet

Voici une application permettant de mieux appréhender à quels moments se produisent les différents événements souris :

Image non disponible
type nom rôle
1 Label lblPositionSouris pour afficher la position de la souris dans le formulaire 1, la liste 2 ou le bouton 3
2 ListBox listBoxEvts pour afficher les évts souris autres que MouseMove
3 Button buttonEffacer pour effacer le contenu de 2

Pour suivre les déplacements de la souris sur les trois contrôles, on n'écrit qu'un seul gestionnaire, le gestionnaire affiche :

Image non disponible

Le code de la procédure affiche est le suivant :

 
CacherSélectionnez

A chaque fois que la souris entre dans le domaine d'un contrôle son système de coordonnées change. Son origine (0,0) est le coin supérieur gauche du contrôle sur lequel elle se trouve. Ainsi à l'exécution, lorsqu'on passe la souris du formulaire au bouton, on voit clairement le changement de coordonnées. Afin de mieux voir ces changements de domaine de la souris, on peut utiliser la propriété Cursor [1] des contrôles :

Image non disponible

Cette propriété permet de fixer la forme du curseur de souris lorsque celle-ci entre dans le domaine du contrôle. Ainsi dans notre exemple, nous avons fixé le curseur à Default pour le formulaire lui-même [2], Hand pour la liste 2 [3] et à Cross pour le bouton 3 [4].

Par ailleurs, pour détecter les entrées et sorties de la souris sur la liste 2, nous traitons les événements MouseEnter et MouseLeave de cette même liste :

 
CacherSélectionnez

Pour traiter les clics sur le formulaire, nous traitons les événements MouseDown et MouseUp :

 
CacherSélectionnez
  • lignes 3 et 8 : les messages sont placés en 1re position dans le ListBox afin que les événements les plus récents soient les premiers dans la liste.
Image non disponible

Enfin, le code du gestionnaire de clic sur le bouton Effacer :

 
CacherSélectionnez

VIII-D. Créer une fenêtre avec menu

Voyons maintenant comment créer une fenêtre avec menu. Nous allons créer la fenêtre suivante :

Image non disponible

Pour créer un menu, on choisit le composant "MenuStrip" dans la barre "Menus & Tollbars" :

Image non disponible
  • [1] : choix du composant [MenuStrip]
  • [2] : on a alors un menu qui s'installe sur le formulaire avec des cases vides intitulées "Type Here". Il suffit d'y indiquer les différentes options du menu.
  • [3] : le libellé "Options A" a été tapé. On passe au libellé [4].
  • [5] : les libellés des options A ont été saisis. On passe au libellé [6]
Image non disponible
  • [6] : les premières options B
  • [7] : sous B1, on met un séparateur. Celui-ci est disponible dans un combo associé au texte "Type Here"
  • [8] : pour faire un sous-menu, utiliser la flèche [8] et taper le sous-menu dans [9]

Il reste à nommer le différents composants du formulaire :

Image non disponible
type nom(s) rôle
1 Label labelStatut pour afficher le texte de l'option de menu cliquée
2 toolStripMenuItem toolStripMenuItemOptionsA
toolStripMenuItemA1
toolStripMenuItemA2
toolStripMenuItemA3
options de menu sous l'option principale "Options A"
3 toolStripMenuItem toolStripMenuItemOptionsB
toolStripMenuItemB1
toolStripMenuItemB2
toolStripMenuItemB3
options de menu sous l'option principale "Options B"
4 toolStripMenuItem toolStripMenuItemB31
toolStripMenuItemB32
options de menu sous l'option principale "B3"

Les options de menu sont des contrôles comme les autres composants visuels et ont des propriétés et événements. Par exemple les propriétés de l'option de menu A1 sont les suivantes :

Image non disponible

Deux propriétés sont utilisées dans notre exemple :

Name le nom du contrôle menu
Text le libellé de l'option de menu

Dans la structure du menu, sélectionnons l'option A1 et cliquons droit pour avoir accès aux propriétés du contrôle :

Image non disponible

Dans l'onglet événements [1], on associe la méthode affiche [2] à l'événement Click. Cela signifie que l'on souhaite que le clic sur l'option A1 soit traitée par une méthode appelée affiche. Visual studio génère automatiquement la méthode affiche dans la fenêtre de code :

 
CacherSélectionnez

Dans cette méthode, nous nous contenterons d'afficher dans le label labelStatut la propriété Text de l'option de menu qui a été cliquée :

 
CacherSélectionnez

La source de l'événement sender est de type object. Les options de menu sont elle de type ToolStripMenuItem, aussi est-on obligé de faire un transtypage de object vers ToolStripMenuItem.

Pour toutes les options de menu, on fixe le gestionnaire du clic à la méthode affiche [3,4].

Exécutons l'application et sélectionnons un élément de menu :

Image non disponible

VIII-E. Composants non visuels

Nous nous intéressons maintenant à un certain nombre de composants non visuels : on les utilise lors de la conception mais on ne les voit pas lors de l'exécution.

VIII-E-1. Boîtes de dialogue OpenFileDialog et SaveFileDialog

Nous allons construire l'application suivante :

Image non disponible

Les contrôles sont les suivants :

type nom rôle
1 TextBox TextBoxLignes texte tapé par l'utilisateur ou chargé à partir d'un fichier
MultiLine=True, ScrollBars=Both, AccepReturn=True, AcceptTab=True
2 Button buttonSauvegarder permet de sauvegarder le texte de [1] dans un fichier texte
3 Button buttonCharger permet de charger le contenu d'un fichier texte dans [1]
4 Button buttonEffacer efface le contenu de [1]
5 SaveFileDialog saveFileDialog1 composant permettant de choisir le nom et l'emplacement du fichier de sauvegarde de [1]. Ce composant est pris dans la barre d'outils [7] et simplement déposé sur le formulaire. Il est alors enregistré mais il n'occupe pas de place sur le formulaire. C'est un composant non visuel.
6 OpenFileDialog openFileDialog1 composant permettant de choisir le fichier à charger dans [1].

Le code associé au bouton Effacer est simple :

 
CacherSélectionnez

Nous utiliserons les propriétés et méthodes suivantes de la classe SaveFileDialog :

Champ Type Rôle
string Filter Propriété les types de fichiers proposés dans la liste déroulante des types de fichiers de la boîte de dialogue
int FilterIndex Propriété le n° du type de fichier proposé par défaut dans la liste ci-dessus. Commence à 0.
string InitialDirectory Propriété le dossier présenté initialement pour la sauvegarde du fichier
string FileName Propriété le nom du fichier de sauvegarde indiqué par l'utilisateur
DialogResult.ShowDialog() Méthode méthode qui affiche la boîte de dialogue de sauvegarde. Rend un résultat de type DialogResult.

La méthode ShowDialog affiche une boîte de dialogue analogue à la suivante :

Image non disponible
1 liste déroulante construite à partir de la propriété Filter. Le type de fichier proposé par défaut est fixé par FilterIndex
2 dossier courant, fixé par InitialDirectory si cette propriété a été renseignée
3 nom du fichier choisi ou tapé directement par l'utilisateur. Sera disponible dans la propriété FileName
4 boutons Enregistrer/Annuler. Si le bouton Enregistrer est utilisé, la fonction ShowDialog rend le résultat DialogResult.OK

La procédure de sauvegarde peut s'écrire ainsi :

 
CacherSélectionnez
  • ligne 4 : on fixe le dossier initial (InitialDirectory) au dossier (Application.ExecutablePath) qui contient l'exécutable de l'application.
  • ligne 5 : on fixe les types de fichiers à présenter. On notera la syntaxe des filtres : filtre1|filtre2|..|filtren avec filtrei= Texte|modèle de fichier. Ici l'utilisateur aura le choix entre les fichiers *.txt et *.*.
  • ligne 6 : on fixe le type de fichier à présenter en premier à l'utilisateur. Ici l'index 0 désigne les fichiers *.txt.
  • ligne 8 : la boîte de dialogue est affichée et son résultat récupéré. Pendant que la boîte de dialogue est affichée, l'utilisateur n'a plus accès au formulaire principal (boîte de dialogue dite modale). L'utilisateur fixe le nom du fichier à sauvegarder et quitte la boîte soit par le bouton Enregistrer, soit par le bouton Annuler, soit en fermant la boîte. Le résultat de la méthode ShowDialog est DialogResult.OK uniquement si l'utilisateur a utilisé le bouton Enregistrer pour quitter la boîte de dialogue.
  • Ceci fait, le nom du fichier à créer est maintenant dans la propriété FileName de l'objet saveFileDialog1. On est alors ramené à la création classique d'un fichier texte. On y écrit le contenu du TextBox : textBoxLignes.Text tout en gérant les exceptions qui peuvent se produire.

La classe OpenFileDialog est très proche de la classe SaveFileDialog. On utilisera les mêmes méthodes et propriétés que précédemment. La méthode ShowDialog affiche une boîte de dialogue analogue à la suivante :

Image non disponible
1 liste déroulante construite à partir de la propriété Filter. Le type de fichier proposé par défaut est fixé par FilterIndex
2 dossier courant, fixé par InitialDirectory si cette propriété a été renseignée
3 nom du fichier choisi ou tapé directement par l'utilisateur. Sera disponible dans la propriété FileName
4 boutons Ouvrir/Annuler. Si le bouton Ouvrir est utilisé, la fonction ShowDialog rend le résultat DialogResult.OK

La procédure de chargement du fichier texte peut s'écrire ainsi :

 
CacherSélectionnez
  • ligne 4 : on fixe le dossier initial (InitialDirectory) au dossier (Application.ExecutablePath) qui contient l'exécutable de l'application.
  • ligne 5 : on fixe les types de fichiers à présenter. On notera la syntaxe des filtres : filtre1|filtre2|..|filtren avec filtrei= Texte|modèle de fichier. Ici l'utilisateur aura le choix entre les fichiers *.txt et *.*.
  • ligne 6 : on fixe le type de fichier à présenter en premier à l'utilisateur. Ici l'index 0 désigne les fichiers *.txt.
  • ligne 8 : la boîte de dialogue est affichée et son résultat récupéré. Pendant que la boîte de dialogue est affichée, l'utilisateur n'a plus accès au formulaire principal (boîte de dialogue dite modale). L'utilisateur fixe le nom du fichier à sauvegarder et quitte la boîte soit par le bouton Ouvrir, soit par le bouton Annuler, soit en fermant la boîte. Le résultat de la méthode ShowDialog est DialogResult.OK uniquement si l'utilisateur a utilisé le bouton Enregistrer pour quitter la boîte de dialogue.

VIII-E-2. Boîtes de dialogue FontColor et ColorDialog

Nous continuons l'exemple précédent en y ajoutant deux nouveaux boutons et deux nouveaux contrôles non visuels :

Image non disponible
type nom rôle
1 Button buttonCouleur pour fixer la couleur des caractères du TextBox
2 Button buttonPolice pour fixer la police de caractères du TextBox
3 ColorDialog colorDialog1 le composant qui permet la sélection d'une couleur - pris dans la boîte à outils [5].
4 FontDialog colorDialog1 le composant qui permet la sélection d'une police de caractères - pris dans la boîte à outils [5].

Les classes FontDialog et ColorDialog ont une méthode ShowDialog analogue à la méthode ShowDialog des classes OpenFileDialog et SaveFileDialog.

La méthode ShowDialog de la classe ColorDialog permet de choisir une couleur [1]. Celle de la classe FontDialog permet de choisir une police de caractères [2] :

Image non disponible
  • [1] : si l'utilisateur quitte la boîte de dialogue avec le bouton OK, le résultat de la méthode ShowDialog est DialogResult.OK et la couleur choisie est dans la propriété Color de l'objet ColorDialog utilisé.
  • [2] : si l'utilisateur quitte la boîte de dialogue avec le bouton OK, le résultat de la méthode ShowDialog est DialogResult.OK et la police choisie est dans la propriété Font de l'objet FontDialog utilisé.

Nous avons désormais les éléments pour traiter les clics sur les boutons Couleur et Police :

 
CacherSélectionnez
  • ligne [4] : la propriété [ForeColor] d'un composant TextBox désigne la couleur de type [Color] des caractères du TextBox. Ici cette couleur est celle choisie par l'utilisateur dans la boîte de dialogue de type [ColorDialog].
  • ligne [12] : la propriété [Font] d'un composant TextBox désigne la police de caractères de type [Font] des caractères du TextBox. Ici cette police est celle choisie par l'utilisateur dans la boîte de dialogue de type [FontDialog].

VIII-E-3. Timer

Nous nous proposons ici d'écrire l'application suivante :

Image non disponible
Type Nom Rôle
1 Label labelChrono affiche un chronomètre
2 Button buttonArretMarche bouton Arrêt/Marche du chronomètre
3 Timer timer1 composant émettant ici un événement toutes les secondes

En [4], nous voyons le chronomètre en marche, en [5] le chronomètre arrêté.

Pour changer toutes les secondes le contenu du Label LabelChrono, il nous faut un composant qui génère un événement toutes les secondes, événement qu'on pourra intercepter pour mettre à jour l'affichage du chronomètre. Ce composant c'est le Timer [1] disponible dans la boîte à outils Components [2] :

Image non disponible

Les propriétés du composant Timer utilisées ici seront les suivantes :

Interval nombre de millisecondes au bout duquel un événement Tick est émis.
Tick l'événement produit à la fin de Interval millisecondes
Enabled rend le timer actif (true) ou inactif (false)

Dans notre exemple le timer s'appelle timer1 et timer1.Interval est mis à 1000 ms (1s). L'événement Tick se produira donc toutes les secondes. Le clic sur le bouton Arrêt/Marche est traité par la procédure buttonArretMarche_Click suivante :

 
CacherSélectionnez
  • ligne 13 : la procédure qui traite le clic sur le bouton Arrêt/Marche.
  • ligne 15 : le libellé du bouton Arrêt/Marche est soit "Arrêt" soit "Marche". On est donc obligé de faire un test sur ce libellé pour savoir quoi faire.
  • ligne 17 : dans le cas de "Marche", on note l'heure de début dans une variable début qui est une variable globale (ligne 11) de l'objet formulaire
  • ligne 19 : initialise le contenu du label LabelChrono
  • ligne 21 : le timer est lancé (Enabled=true)
  • ligne 23 : libellé du bouton passe à "Arrêt".
  • ligne 27 : dans le cas de "Arrêt"
  • ligne 29 : on arrête le timer (Enabled=false)
  • ligne 31 : on passe le libellé du bouton à "Marche".

Il nous reste à traiter l'événement Tick sur l'objet timer1, événement qui se produit toutes les secondes :

 
CacherSélectionnez
  • ligne 3 : on note l'heure du moment
  • ligne 4 : on calcule le temps écoulé depuis l'heure de lancement du chronomètre. On obtient un objet de type TimeSpan qui représente une durée dans le temps.
  • ligne 6 : celle-ci doit être affichée dans le chronomètre sous la forme hh:mm:ss. Pour cela nous utilisons les propriétés Hours, Minutes, Seconds de l'objet TimeSPan qui représentent respectivement les heures, minutes, secondes de la durée que nous affichons au format ToString("d2") pour avoir un affichage sur 2 chiffres.

VIII-F. Application exemple - version 6

On reprend l'application exemple IMPOTS. La dernière version a été étudiée au paragraphe , page . C'était l'application à trois couches suivante :

Image non disponible
  • les couches [metier] et [dao] étaient encapsulées dans des DLL
  • la couche [ui] était une couche [console]
  • l'instanciation des couches et leur intégration dans l'application étaient assurées par Spring.

Dans cette nouvelle version, la couche [ui] sera assurée par l'interface graphique suivante :

Image non disponible

VIII-F-1. La solution Visual Studio

La solution Visual Studio est composée des éléments suivants :

Image non disponible
  • [1] : le projet est constitué des éléments suivants :
  • [Program.cs] : la classe qui lance l'application
  • [Form1.cs] : la classe d'un 1er formulaire
  • [Form2] : la classe d'un 2ième formulaire
  • [lib] détaillé dans [2] : on y a mis toutes les DLL nécessaires au projet :
  • [ImpotsV5-dao.dll] : la DLL de la couche [dao] générée au paragraphe , page
  • [ImpotsV5-metier.dll] : la DLL de la couche [dao] générée au paragraphe , page
  • [Spring.Core.dll], [Common.Logging.dll], [antlr.runtime.dll] : les DLL de Spring déjà utilisées dans la version précédente (cf paragraphe , page ).
  • [references] détaillé dans [3] : les références du projet. On a ajouté une référence pour chacune des DLL du dossier [lib]
  • [App.config] : le fichier de configuration du projet. Il est identique à celui de la version précédente décrit au paragraphe , page .
  • [DataImpot.txt] : le fichier des tranches d'impôt configuré pour être recopié automatiquement dans le dossier d'exécution du projet [4]

Le formulaire [Form1] est le formulaire de saisies des paramètres du calcul de l'impôt [A] déjà présenté plus haut. Le formulaire [Form2] [B] sert à afficher un message d'erreur :

Image non disponible

VIII-F-2. La classe [Program.cs]

La classe [Program.cs] lance l'application. Son code est le suivant :

 
CacherSélectionnez

Le code généré par Visual Studio a été complété à partir de la ligne 19. L'application exploite le fichier [] suivant :

 
CacherSélectionnez
  • lignes 24-32 : exploitation du fichier [App.config] précédent pour instancier les couches [metier] et [dao]
  • ligne 26 : exploitation du fichier [App.config]
  • ligne 28 : récupération d'une référence sur la couche [metier]
  • ligne 31 : mémorisation de l'éventuelle exception
  • ligne 34 : la référence form désignera le formulaire à afficher (form1 ou form2)
  • lignes 36-50 : s'il y a eu exception, on se prépare à afficher un formulaire de type [Form2]
  • lignes 38-44 : on construit le message d'erreur à afficher. Il est constitué de la concaténation des messages d'erreurs des différentes exceptions présentes dans la chaîne des exceptions.
  • ligne 46 : un formulaire de type [Form2] est créé.
  • ligne 47 : nous le verrons ultérieurement, ce formulaire a une propriété publique MsgErreur qui est le message d'erreur à afficher :
 
CacherSélectionnez

On renseigne cette propriété.

  • ligne 49 : la référence form qui désigne la fenêtre à afficher est initialisée. On notera le polymorphisme à l'œuvre. form2 n'est pas de type [Form] mais de type [Form2], un type dérivé de [Form].
  • lignes 50-57 : il n'y a pas eu d'exception. On se prépare à afficher un formulaire de type [Form1].
  • ligne 53 : un formulaire de type [Form1] est créé.
 
CacherSélectionnez

On renseigne cette propriété.

  • ligne 56 : la référence form qui désigne la fenêtre à afficher est initialisée. On notera de nouveau le polymorphisme à l'œuvre. form1 n'est pas de type [Form] mais de type [Form1], un type dérivé de [Form].
  • ligne 59 : la fenêtre référencée par form est affichée.

VIII-F-3. Le formulaire [Form1]

En mode [conception] le formulaire [Form1] est le suivant :

Image non disponible

Les contrôles sont les suivants

type nom rôle
0 GroupBox groupBox1 Text=Etes-vous marié(e)
1 RadioButton radioButtonOui coché si marié
2 RadioButton radioButtonNon coché si pas marié
Checked=True
3 NumericUpDown numericUpDownEnfants nombre d'enfants du contribuable
Minimum=0, Maximum=20, Increment=1
4 TextBox textSalaire salaire annuel du contribuable en euros
5 Label labelImpot montant de l'impôt à payer
BorderStyle=Fixed3D
6 Button buttonCalculer lance le calcul de l'impôt
7 Button buttonEffacer remet le formulaire dans l'état qu'il avait lors du chargement
8 Button buttonQuitter pour quitter l'application

Règles de fonctionnement du formulaire

  • le bouton Calculer reste éteint tant qu'il n'y a rien dans le champ du salaire
  • si lorsque le calcul est lancé, il s'avère que le salaire est incorrect, l'erreur est signalée [9]

Le code de la classe est le suivant :

 
CacherSélectionnez

Nous ne commentons que les parties importantes :

  • ligne [8] : la propriété publique Metier qui permet à la classe de lancement [Program.cs] d'injecter dans [Form1] une référence sur la couche [metier].
  • ligne [14] : la procédure de calcul de l'impôt
  • lignes 15-27 : vérification de la validité du salaire (un nombre entier >=0).
  • ligne 29 : calcul de l'impôt à l'aide de la méthode [CalculerImpot] de la couche [metier]. On notera la simplicité de cette opération obtenue grâce à l'encapsulation de la couche [metier] dans une DLL.

VIII-G. Le formulaire [Form2]

En mode [conception] le formulaire [Form2] est le suivant :

Image non disponible

Les contrôles sont les suivants

type nom rôle
1 TextBox textBoxErreur Multiline=True, Scrollbars=Both

Le code de la classe est le suivant :

 
CacherSélectionnez
  • ligne 6 : la propriété publique MsgErreur qui permet à la classe de lancement [Program.cs] d'injecter dans [Form2] le message d'erreur à afficher. Ce message est affiché lors du traitement de l'événement Load, lignes 12-16.
  • ligne 14 : le message d'erreur est mis dans le TextBox
  • ligne 16 : on enlève la sélection qui a lieu lors de l'opération précédente. [TextBox].Select(début,longueur) sélectionne (mise en surbrillance) longueur caractères à partir du caractère n° début. [TextBox].Select(0,0) revient à désélectionner tout texte.

VIII-G-1. Conclusion

Revenons sur l'architecture trois couches utilisée :

Image non disponible

Cette architecture nous a permis de substituer une implémentation graphique à l'implémentation console de la couche [ui] existante, sans rien changer aux couches [metier] et [dao]. Nous avons pu nous concentrer sur la couche [ui] sans nous inquiéter des impacts possibles sur les autres couches. C'est là le principal intérêt des architectures 3 couches. Nous en verrons un autre exemple plus loin, lorsque la couche [dao] qui exploite actuellement les données d'un fichier texte, sera remplacée par une couche [dao] exploitant les données d'une base de données. Nous verrons que cela se fera sans impact sur les couches [ui] et [metier].


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Serge Tahé. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.