Chapitre 214 minIntermédiaire
Les contrôles WinForms : TextBox, ComboBox, DataGridView
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- nommer et régler les contrôles avec une convention claire ;
- remplir une ComboBox à partir d'une liste d'objets et lire la valeur choisie ;
- choisir le bon contrôle pour chaque donnée à saisir ;
- construire une fenêtre qui reste lisible quand on l'agrandit.
4 exercices corrigés
Un contrôle est un élément visuel posé sur un formulaire : une étiquette, une zone de texte, un bouton, une liste déroulante, un tableau. Chaque contrôle est un objet C# dont les propriétés règlent l'apparence et le comportement, et dont les événements signalent les actions de l'utilisateur. Ce chapitre présente les contrôles qu'on retrouve dans toutes les applications de gestion, avec les propriétés qui comptent et les pièges de chacun.
Poser un contrôle et régler ses propriétés
La Boîte à outils (Ctrl + Alt + X) liste les contrôles. Fais-en glisser un sur le formulaire, puis règle-le dans la fenêtre Propriétés (F4). Les propriétés communes à presque tous les contrôles :
| Propriété | Rôle | Exemple |
|---|---|---|
(Name) | Le nom du champ dans le code | txtNom |
Text | Le texte affiché | Enregistrer |
Enabled | Utilisable ou grisé | false pendant un chargement |
Visible | Affiché ou caché | false pour un bouton réservé à l'administrateur |
TabIndex | L'ordre de passage avec la touche Tab | 0, 1, 2… de haut en bas |
Anchor, Dock | Le comportement quand la fenêtre change de taille | voir plus bas |
Font, ForeColor, BackColor | Police et couleurs | à éviter sauf besoin réel |
Tout ce qui se règle dans la fenêtre Propriétés se règle aussi par le code, et inversement : btnEnregistrer.Enabled = false; a le même effet que de mettre Enabled à False dans la fenêtre, mais à l'exécution.
Nommer les contrôles
Le concepteur nomme les contrôles textBox1, textBox2, button1. Trois semaines plus tard, plus personne ne sait lequel contient le nom du patient. Renomme chaque contrôle dès que tu le poses, avant d'écrire le moindre gestionnaire : renommer après coup laisse des gestionnaires au nom périmé (button1_Click).
La convention la plus répandue préfixe le nom par trois lettres qui disent le type de contrôle :
| Contrôle | Préfixe | Exemple |
|---|---|---|
Label | lbl | lblStatut |
TextBox | txt | txtNom |
Button | btn | btnEnregistrer |
ComboBox | cbo | cboMedecin |
ListBox | lst | lstMedicaments |
CheckBox | chk | chkSurOrdonnance |
RadioButton | rdo | rdoHomme |
DateTimePicker | dtp | dtpNaissance |
NumericUpDown | nud | nudDuree |
DataGridView | dgv | dgvPatients |
GroupBox, Panel | grp, pnl | grpIdentite |
| Formulaire | Frm | FrmPatients |
Une autre convention, recommandée par certaines équipes, met le type à la fin (nomTextBox, enregistrerButton). Peu importe laquelle, pourvu qu'elle soit la même dans tout le projet : la commission d'E6 lira ton code.
Afficher et saisir du texte : Label et TextBox
Le Label affiche un texte que l'utilisateur ne modifie pas : un libellé de champ, un message de statut. Mets AutoSize à true pour qu'il prenne la taille de son texte.
La TextBox est la zone de saisie. Sa propriété Text contient toujours du texte, même quand l'utilisateur tape un nombre : il faudra le convertir (chapitre 5).
| Propriété | Usage |
|---|---|
MaxLength | Longueur maximale, à aligner sur la colonne SQL (VARCHAR(50) → 50) |
PlaceholderText | Texte grisé affiché quand la zone est vide : « Rechercher un nom » |
Multiline, ScrollBars | Plusieurs lignes : une observation, une adresse |
ReadOnly | Affiché, sélectionnable, mais non modifiable |
UseSystemPasswordChar | Masque la saisie par des points : mot de passe |
CharacterCasing | Force les majuscules ou les minuscules |
txtNom.MaxLength = 50;
txtSecu.MaxLength = 13;
txtMotDePasse.UseSystemPasswordChar = true;
string nom = txtNom.Text.Trim(); // Trim retire les espaces au début et à la fin
Déclencher une action : Button
Le Button déclenche une action par son événement Click. Deux réglages du formulaire rendent la saisie bien plus agréable :
AcceptButtondu formulaire désigne le bouton activé par la touche Entrée (Enregistrer, Se connecter) ;CancelButtondésigne le bouton activé par Échap (Annuler, Fermer).
Une esperluette dans le texte crée un raccourci clavier : &Enregistrer affiche « Enregistrer » avec le E souligné, et Alt + E clique sur le bouton.
Choisir dans une liste : ComboBox et ListBox
La ComboBox est une liste déroulante. Avec DropDownStyle = DropDownList, l'utilisateur ne peut que choisir une valeur proposée : c'est ce qu'il faut pour choisir le médecin d'une ordonnance. Avec le style DropDown par défaut, il peut aussi taper n'importe quoi, ce qui est rarement souhaité.
Le plus souvent, on remplit la liste avec des objets et non des chaînes. Trois propriétés font le lien :
DataSource: la liste d'objets ;DisplayMember: le nom de la propriété affichée ;ValueMember: le nom de la propriété renvoyée parSelectedValue, en général l'identifiant.
List<Medecin> medecins = await _medecinService.ListerAsync();
cboMedecin.DropDownStyle = ComboBoxStyle.DropDownList;
cboMedecin.DisplayMember = nameof(Medecin.NomComplet); // « Dr Paul Martin »
cboMedecin.ValueMember = nameof(Medecin.NumMedecin); // 1
cboMedecin.DataSource = medecins;
Pour lire le choix, SelectedValue renvoie l'identifiant et SelectedItem l'objet entier :
if (cboMedecin.SelectedValue is int numMedecin)
{
ordonnance.NumMedecin = numMedecin;
}
Medecin? medecin = cboMedecin.SelectedItem as Medecin;
nameof(Medecin.NomComplet) produit la chaîne "NomComplet", mais vérifiée par le compilateur : si tu renommes la propriété, le code ne compile plus au lieu d'afficher une liste vide.
La ListBox affiche plusieurs éléments à la fois et peut autoriser la sélection multiple (SelectionMode = MultiExtended), lue par SelectedItems. Elle convient pour une liste courte : les médicaments d'une ordonnance, par exemple.
Cases à cocher et boutons radio
La CheckBox répond à une question oui ou non : sa propriété Checked est un bool. Les RadioButton proposent un choix exclusif : dans un même conteneur, cocher l'un décoche les autres. Pour avoir deux groupes indépendants sur un formulaire, place chaque groupe dans son propre GroupBox ou Panel.
bool surOrdonnance = chkSurOrdonnance.Checked;
string sexe = rdoFemme.Checked ? "F" : "M";
Saisir une date ou un nombre sans conversion
Plutôt que de faire taper une date dans une TextBox puis de la convertir, utilise un DateTimePicker. Sa propriété Value est un DateTime toujours valide. Règle Format = Short pour afficher 12/05/1990, et MaxDate = DateTime.Today pour interdire une naissance future.
Une date facultative (la date de naissance peut être inconnue) se gère avec ShowCheckBox = true : une case apparaît dans le contrôle, et Checked dit si une date a été choisie.
dtpNaissance.Format = DateTimePickerFormat.Short;
dtpNaissance.MaxDate = DateTime.Today;
dtpNaissance.ShowCheckBox = true;
DateTime? naissance = dtpNaissance.Checked ? dtpNaissance.Value.Date : null;
Le NumericUpDown fait de même pour les nombres : Minimum, Maximum, DecimalPlaces et Increment bornent la saisie, et Value est un decimal toujours compris entre les bornes. La durée d'un traitement, de 1 à 90 jours, s'y prête parfaitement.
nudDuree.Minimum = 1;
nudDuree.Maximum = Ordonnance.DureeMaxJours; // 90
int duree = (int)nudDuree.Value;
Afficher un tableau : DataGridView
Le DataGridView affiche des données en lignes et en colonnes. C'est le contrôle central d'une application de gestion : la liste des patients, des ordonnances, des médicaments. Sa liaison aux données fait l'objet du chapitre 6 ; voici les réglages à faire dès qu'on le pose.
| Propriété | Valeur conseillée | Pourquoi |
|---|---|---|
ReadOnly | true | On modifie dans un formulaire dédié, pas dans la grille |
AllowUserToAddRows | false | Sinon une ligne vide « nouvelle ligne » apparaît en bas |
AllowUserToDeleteRows | false | La suppression passe par un bouton et une confirmation |
SelectionMode | FullRowSelect | Un clic sélectionne la ligne entière |
MultiSelect | false | Une seule ligne à la fois |
AutoSizeColumnsMode | Fill | Les colonnes occupent toute la largeur |
RowHeadersVisible | false | Retire la colonne grise de gauche |
Menus, barres d'outils et barre d'état
MenuStrip: la barre de menus (Fichier, Patients, Ordonnances, Aide). Chaque élément est unToolStripMenuItemavec un événementClicket une propriétéShortcutKeys(Ctrl+Npour Nouveau).ToolStrip: la barre de boutons sous le menu.StatusStrip: la barre d'état en bas de la fenêtre, avec unToolStripStatusLabelpour afficher « 3 patients » ou « Connecté : secretariat ».TabControl: des onglets, pour séparer l'identité d'un patient de ses ordonnances sans ouvrir une autre fenêtre.
Disposer les contrôles : Anchor, Dock et les conteneurs
Par défaut, un contrôle reste collé à son point en haut à gauche : quand l'utilisateur agrandit la fenêtre, la grille ne grandit pas et les boutons restent au milieu. Deux propriétés règlent ce comportement.
Anchoraccroche le contrôle à un ou plusieurs bords. Une grille ancréeTop, Bottom, Left, Rights'étire dans les deux sens ; un bouton ancréBottom, Rightreste dans le coin en bas à droite.Dockcolle le contrôle contre un bord et lui fait occuper toute la largeur ou la hauteur :Toppour une barre de recherche,Fillpour la grille qui prend le reste.
Pour des formulaires de saisie bien alignés, utilise un TableLayoutPanel : une grille invisible, avec une colonne pour les libellés et une pour les champs. Un FlowLayoutPanel aligne des boutons les uns à la suite des autres. Ces conteneurs gèrent aussi les différences de police et de résolution, là où des positions fixées au pixel se décalent.
Le mode sombre
Depuis .NET 10, le mode sombre de WinForms n'est plus expérimental. Une ligne dans Program.cs, avant de créer la première fenêtre, fait suivre à l'application le réglage clair ou sombre de Windows :
ApplicationConfiguration.Initialize();
Application.SetColorMode(SystemColorMode.System); // ou Dark, ou Classic
Les couleurs fixées à la main (BackColor = Color.White) ne suivent pas : c'est une raison de plus pour laisser les couleurs par défaut.
Un formulaire accessible
Une application de gestion est utilisée au clavier toute la journée. Vérifie que la touche Tab parcourt les champs dans l'ordre de lecture (menu Affichage > Ordre de tabulation dans le concepteur), que les boutons principaux ont un raccourci Alt, et que AcceptButton et CancelButton sont réglés. Pour un lecteur d'écran, renseigne AccessibleName sur les contrôles sans libellé visible, comme un bouton qui n'affiche qu'une icône.
Exercices
Exercice 1 sur 4 Concevoir le formulaire patient Réussi À revoir
Le formulaire d'édition d'un patient doit permettre de saisir le nom (obligatoire, 50 caractères au plus), le prénom (facultatif), la date de naissance (facultative, jamais dans le futur) et le numéro de sécurité sociale (13 chiffres, facultatif), puis d'enregistrer ou d'annuler. Pour chaque élément, donne le type de contrôle, son nom et les propriétés à régler.
Voir la réponseMasquer la réponse de l'exercice 1
| Élément | Contrôle | Nom | Propriétés |
|---|---|---|---|
| Nom | TextBox | txtNom | MaxLength 50, TabIndex 0 |
| Prénom | TextBox | txtPrenom | MaxLength 50, TabIndex 1 |
| Naissance | DateTimePicker | dtpNaissance | Format Short, ShowCheckBox true, MaxDate = aujourd'hui (par le code, au chargement) |
| N° de sécurité sociale | TextBox | txtSecu | MaxLength 13 |
| Enregistrer | Button | btnEnregistrer | Text &Enregistrer, désigné comme AcceptButton du formulaire |
| Annuler | Button | btnAnnuler | DialogResult Cancel, désigné comme CancelButton |
Plus un Label devant chaque champ (« Nom * », « Prénom »…), le tout dans un TableLayoutPanel à deux colonnes. MaxDate se règle dans le code, car « aujourd'hui » change chaque jour.
Tu avais trouvé ?
Exercice 2 sur 4 La liste des médecins Réussi À revoir
medecins est une List<Medecin>, la classe Medecin a les propriétés NumMedecin (int) et NomComplet (string). Écris le code qui remplit cboMedecin pour qu'on ne puisse que choisir un médecin, puis le code qui affiche dans lblChoix le numéro du médecin choisi.
Voir la réponseMasquer la réponse de l'exercice 2
cboMedecin.DropDownStyle = ComboBoxStyle.DropDownList;
cboMedecin.DisplayMember = nameof(Medecin.NomComplet);
cboMedecin.ValueMember = nameof(Medecin.NumMedecin);
cboMedecin.DataSource = medecins;
// plus tard, par exemple dans le Click d'un bouton
if (cboMedecin.SelectedValue is int numMedecin)
{
lblChoix.Text = $"Médecin n° {numMedecin}";
}
Le test is int numMedecin protège du cas où aucun médecin n'est sélectionné (liste vide) : SelectedValue vaut alors null.
Tu avais trouvé ?
Exercice 3 sur 4 Le bon contrôle Réussi À revoir
Choisis le contrôle le plus adapté à chaque saisie, et dis pourquoi.
- La spécialité d'un médecin, parmi une liste fixe de 12 spécialités.
- Le nombre de boîtes à délivrer, de 1 à 6.
- Une observation médicale de plusieurs lignes.
- Le choix « médicament remboursé » oui ou non.
- La date d'une ordonnance.
Voir la réponseMasquer la réponse de l'exercice 3
ComboBoxenDropDownList: l'utilisateur ne peut pas inventer une spécialité.NumericUpDownavecMinimum1 etMaximum6 : aucune conversion, aucune valeur hors limites.TextBoxavecMultilineàtrueet une barre de défilement verticale.CheckBox: unbool.DateTimePicker: la valeur est toujours une date valide.
Tu avais trouvé ?
Exercice 4 sur 4 La fenêtre qui ne s'agrandit pas bien Réussi À revoir
La fenêtre FrmPatients contient en haut une zone de recherche, au milieu une grille, en bas à gauche trois boutons (Nouveau, Modifier, Supprimer) et en bas à droite un bouton Actualiser. Quand on l'agrandit, la grille garde sa taille et les boutons restent au milieu. Donne la valeur d'Anchor de chaque contrôle.
Voir la réponseMasquer la réponse de l'exercice 4
- Zone de recherche :
Top, Left(ouTop, Left, Rightsi elle doit s'élargir). - Grille :
Top, Bottom, Left, Right: elle s'étire dans les deux sens. - Nouveau, Modifier, Supprimer :
Bottom, Left: ils suivent le bas de la fenêtre. - Actualiser :
Bottom, Right: il reste dans le coin.
Tu avais trouvé ?