|
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 SSAMECI (Université Paris 7) |
||||
|
Partie 7
Encore des contrôles (les listes)
Reprenons à présent notre tour d'horizon des différents
contrôles proposés par VB.Net. Bon, les Button, c'est fait. Les Label et les Textbox, on les a vues aussi, hmmm... les cases, ça y est... que
reste-t-il ? Oh, tiens, ben oui :
1. La classe Listbox
La Listbox est à l'ensemble des classes de listes ce qu'est la 2 CV aux
automobiles : un prototype ancien et basique, mais solide, rustique, et
qu'on a toujours plaisir à voir en service :
![]() La Listbox, par définition :
Pour les Listbox, on va trouver
plusieurs propriétés concernant le
mode d'affichage, dont entre autres :
Et une propriété fondamentale pour son comportement :
En ce qui concerne la gestion des items de la ListBox,
il faut avoir à
l'esprit une chose, de laquelle se déduisent toutes les autres :
Un item d'une Listbox est une chaîne de caractères, membre de la collection Items membre de la Listbox. Par conséquent, ce que nous avons appris au chapitre
précédent sur
les collections s'applique de plein droit aux items des Listbox.
Comme quoi, on dira ce qu'on voudra, mais au niveau de l'articulation
pédagogique, on sent bien que ce cours est drôlement bien goupillé et
qu'on a pas affaire à un amateur. Enfin,
moi, ce que j'en dis, hein...
Pour balayer les éléments d'une Listbox du
premier au dernier, nous pourrons donc utiliser leurs numéros d'indice.
Attention toutefois,
Listbox n'autorise pas l'omission de la propriété Items,
qu'il faudra donc stipuler en toutes lettres. Pour passer l'ensemble
des éléments
d'une Listbox en majuscules, on pourra donc écrire :
Dim i As Integer
For i = 0 To ListBox1.Items.Count - 1 ListBox1.Items(i) = ListBox1.Items(i).ToUpper Next i Dans une Listbox, les items sont numérotés à partir de l'indice zéro On pourra également, dans d'autres circonstances, utiliser la boucle
For Each ... Next, par exemple pour récupérer tout le contenu de la
liste dans une seule chaîne MaListe :
Dim element As Object
Dim MaListe As String MaListe = "" For Each element In ListBox1.Items MaListe = MaListe & element Next element On peut, comme dans n'importe quelle collection, ajouter un élément
dans une liste via la méthode Add :
Listbox1.Items.Add("Nouvel élément")
Pour supprimer un élément, on n'a que l'embarras du
choix. La méthode
Remove demandera de fournir l'élément lui-même...
Listbox1.Items.Remove("Midnight Jokers")
...tandis que la méthode RemoveAt demandera
un indice :
Listbox1.Items.RemoveAt(5)
Enfin, Clear procèdera au nettoyage complet de la liste, en
supprimant tous les éléments d'un seul coup d'un seul :
Listbox1.Items.Clear
Le but d'une liste, c'est de permettre à l'utilisateur d'en
sélectionner un ou plusieurs items. Cette action de l'utilisateur
affectera les propriétés :
Lorsqu'une sélection multiple est possible sur une Listbox, ces deux
propriétés renvoient alors des collections, dans lesquelles on peut
partir à la pêche pour récupérer les différents éléments.
En ce qui concerne les événements, on peut bien sûr gérer les Listbox par l'événement Click. Mais un événement propre est disponible, qui ne
se déclenche qu'en cas de changement de l'item sélectionné : il s'agit
de SelectedIndexChanged. Cet événement est à la fois plus
restrictif et plus large que le Click. Plus restrictif, car il ne se
déclenche pas en cas de clic sur un item déjà sélectionné. Plus large,
car il détectera un changement de sélection survenant y compris suite à
une manoeuvre au clavier.
Bon, c'est pas tout ça, mais on va pouvoir utiliser notre science
toute neuve :
Cet exercice n'est pas spécialement facile. Afin de
graduer la difficulté, on pourra commencer par en créer une version
qui ne se préoccupe pas de la multisélection. Une fois que c'est au
point, on introduira la case à cocher, et on modifiera le code là où
c'est nécessaire.
2. La classe Combobox
Il s'agit d'une classe-fille à la fois de la classe Listbox et
de la classe Textbox. Elle en hérite
donc l'essentiel des propriétés. En gros, on peut considérer
qu'une ComboBox,
c'est une liste modifiable et/ou déroulante :
![]() L'aspect de la ComboBox dépend de la valeur donnée
à la propriété DropDownStyle :
Je rappelle que le caractère déroulant
d'une Combobox n'influence que son aspect, alors que son caractère
modifiable influence son comportement (peut-on ou non y entrer
autre chose qu'un item de la liste).
3. La classe CheckedListBox Cette classe est une espèce de croisement
entre la
Listbox et la Checkbox.
De la première, elle hérite
toutes les propriétés hormis celles liées à la
multisélection.
Cette classe
affiche une liste dans laquelle chaque élément
est représenté avec
une case à cocher :
![]() A noter que :
Comme ça, ça a l'air compliqué, mais c'est plus laborieux que
vraiment méchant.
4. La classe ImageList
Ce contrôle n'a aucun intérêt par lui-même. Mais
associé à d'autres (que nous verrons dans un instant), il fait
un malheur.
Son rôle consiste à être en quelque
sorte un tableau d'images pour que d'autres contrôles viennent
puiser dedans. Il
s'agit d'un contrôle invisible. Ce n'est pas le seul,
mais c'est le premier que nous rencontrons. Encore une fois, il
ne sert que
d'espace de stockage. D'ailleurs, lorsque que nous le sélectionnons
dans la boîte à outils, nous voyons tout de suite
qu'il ne se pose pas sur la Form, mais à côté.
On remplit le contrôle ImageList par sa propriété
Images, qui désigne une collection (et à laquelle s'appliquent
donc les méthodes Add et Remove, si on veut en manipuler le contenu
par du code). La propriété ColorDepth permet de régler la qualité
de codage des
images en
question,
et roule
Raoul : on accède ensuite aux images que contient ImageList par
la propriété ImageIndex.
Ayé, on a fait le tour de ce petit contrôle.
5. La classe Listview Il s'agit d'une liste pouvant présenter les informations
qu'elle contient de quatre manières possibles, imitant parfaitement
le volet droit de l'explorateur Windows (grandes icônes, petites
icônes, liste, détails). Ces modes d'affichage sont réglés par
la propriété View. On peut également faire apparaître une case
à cocher devant chaque item via la propriété booléenne Checkboxes.
Le maniement de cette classe étant assez particulier,
je vous renvoie en cas de besoin à un ouvrage spécialisé (pour
l'heure, le courage me manque de rédiger cette partie ; on a beau
être enseignant, c'est-à-dire surhomme, on n'en a pas moins ses petits
moments de faiblesse).
6. La classe Treeview La présentation de la classe Treeview et des exemples de code qui vont avec constitue indéniablement un des moments les plus pénibles de ce cours qui en compte pourtant beaucoup. Aussi difficile à lire pour vous qu'elle l'a été à écrire pour moi, cette partie pourra être ignorée par tous ceux qui n'en ont pas un besoin absolu, ou qui n'égayent pas leurs longues soirées d'hiver en s'adonnant aux pratiques masochistes. "Back to the trees !" criait
le pithécanthrope réactionnaire
qui refusait la bipédie dans Pourquoi
j'ai mangé mon père. Ben, à ce qu'il semble,
après quelques millions d'années d'évolution
ayant culminé dans le VB.Net, il est effectivement temps d'y retourner.
Dans les arbres, nous allons pouvoir faire plein
de choses. En effet, les arbres sont une manière extrêmement
pratique de structurer les données, et au moment même
où vous lisez ces
lignes, je suis certain que vous avez déjà en tête plusieurs
exemples de situations où des données informatiques
sont organisées - et peuvent donc être représentées
- sous forme d'arbre.
L'exemple le plus tartignol - c'est évidemment celui que
je choisirai - est celui d'un disque dur, avec ses répertoires
contenant d'autres répertoires,
qui contiennent d'autres répertoires, etc. Et d'ailleurs,
si vous voulez imaginer à quoi ressemble un contrôle
Treeview, il suffit de penser à la partie gauche de l'explorateur
de Windows. Ce n'est pas pour rien.
Alors, le Tree d'un Treeview, c'est quoi ? C'est
un sac de noeuds, au propre comme au figuré.
Chaque noeud (Node,
en anglais, objet de la classe TreeNode) fait partie d'une
collection (TreeNodeCollection) des noeuds qui ont le même noeud
parent que lui (tous les sous-répertoires
immédiats
d'un même répertoire).
Dans l'autre sens, chaque noeud pointe sur une collection,
celle des noeuds dont il est lui-même le père (tous ses sous-répertoires
immédiats).
Manipuler un Treeview,
cela revient donc à manipuler des collections de noeuds ! Ce qui signifie
:
Côté événements, les Treeview proposent plusieurs
possibilités originales. On peut notamment gérer le fait qu'un
noeud soit déployé, par les événements BeforeExpand ou AfterExpand (selon qu'on
veuille agir juste avant ou juste après le déploiement du noeud).
On peut également gérer le fait qu'un noeud soit replié, avec
les événements BeforeCollapse
et AfterCollapse (même différence entre les deux).
L'exemple de programmation qui va suivre est pour ainsi dire complètement idiot, car comme nous le verrons plus loin, il existe un moyen beaucoup plus rapide de parvenir au résultat dont nous allons laborieusement accoucher ici. Mais, et les mauvais esprits n'ont qu'à bien se tenir, cet exemple possède une vertu pédagogique, dans la mesure où il nous permettra de mettre le doigt dans des techniques un peu... euh, disons... stimulantes. Bon, on sait maintenant presque assez pour s'amuser
à fabriquer une réplique grandeur nature de la partie gauche de
l'explorateur Windows. Pour cela, il nous manque encore quelques
petits détails. En particulier, il nous faut un moyen de récupérer,
à partir d'un répertoire donné, la liste des sous-répertoires qu'il
contient. Ça, ça ne s'invente pas, et c'est pour cela qu'existe
la fonction :
Directory.GetDirectories(répertoire)
qui renvoie, sous forme d'un tableau de chaînes,
l'ensemble des sous-répertoires du répertoire passé en
argument. Sauf que pour que ça marche, il ne faudra pas oublier
d'écrire, tout en haut du programme :
Imports System.IO
Parce que sinon, le compilateur ne sait pas qui
est ce Directory dont vous lui parlez, encore moins quelle est
cette méthode GetDirectories qui lui est attachée, et vous flanquera
illico une erreur de compilation. Rappelez-vous, les espaces de
noms, nous en avons déjà parlé...
Allons-y maintenant pour de bon. Notre code va s'articuler
autour d'une petite procédure que nous appellerons chaque fois
que nous en aurons besoin pour mettre à jour notre Treeview. Cette
procédure que j'appelle Explor :
La voici, la voilà :
Private Sub Explor(ByVal Node As TreeNode)
Node.Nodes.Clear() Dim s As String For Each s In Directory.GetDirectories(Node.FullPath) Node.Nodes.Add(Path.GetFileName(s)) Next s End Sub Il faut remarquer que s récupère à chaque fois le
nom complet du répertoire (avec le chemin). D'où l'emploi de la
fonction Path.GetFileName, qui purge le chemin et nous restitue
le nom du répertoire proprement dit.
Et d'une.
Et de deux, il faut qu'en cas de clic sur un noeud,
notre procédure Explor soit déclenchée
pour chacun des sous-répertoires
du noeud en question (on a en quelque sorte à chaque
fois un coup d'avance : on affiche dans l'arbre les répertoires
situés deux
niveaux en-dessous du répertoire actif : comme cela,
parmi les sous-répertoires directs du sous-répertoire
actif, le Treeview affichera différemment ceux qui contiennent
quelque chose et ceux qui ne contiennent rien. On aura donc
:
Private Sub TreeView1_AfterExpand(ByVal sender
As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles
TreeView1.AfterExpand
Dim z As TreeNode For Each z In e.Node.Nodes Explor(z) Next z End Sub Il ne manque plus qu'un détail : amorcer la pompe
lors du chargement de la Form, en donnant comme point de départ
la racine du lecteur C:, et en considérant que le premier - et
le seul - noeud présent dans notre Treeview porte l'index zéro
:
Private Sub Form1_Load(ByVal sender As Object,
ByVal e As System.EventArgs) Handles MyBase.Load
TreeView1.Nodes.Add("c:\") Explor(TreeView1.Nodes.Item(0)) End Sub Avec tout cela, on obtient un joli explorateur qui
fonctionne, en nous donnant un truc dans ce genre :
![]() Bien évidemment, on pourrait programmer l'affaire très différemment, par exemple en remplissant d'avance, une bonne fois pour toutes, le Treeview avec tous les répertoires du disque dur. Mais ça ne serait pas plus facile à programmer, et cela s'avèrerait en réalité moins performant. Pour que notre explorateur soit parfait, nickel-chrome
comme ceux vus à la TV, il lui manque toutefois un petit détail
: qu'à chaque noeud soit associé non seulement le petit "+" ou
la simple ligne, mais également l'icône du répertoire
ouvert ou fermé, selon les circonstances.
C'est ici que le contrôle ImageList, dont nous avons
parlé juste avant, va nous rendre un fier service. Il nous suffit
d'aller attraper quelque part deux jolies images, l'une d'un répertoire
ouvert, l'autre d'un répertoire fermé, et de les mettre dans un
contrôle ImageList.
Ensuite, associons notre contrôle ImageList au Treeview,
par la propriété... ImageList de ce dernier.
A présent, modifions légèrement notre procédure Explor,
en ajoutant deux instructions :
Private Sub Explor(ByVal Node As TreeNode)
Node.Nodes.Clear() Dim s As String Dim z As TreeNode For Each s In Directory.GetDirectories(Node.FullPath) z = Node.Nodes.Add(Path.GetFileName(s)) z.ImageIndex = 1 z.SelectedImageIndex = 0 Next s End Sub Il ne reste plus qu'à faire de même dans la procédure
Form1_Load, et le tour est joué :
Private Sub Form1_Load(ByVal sender As Object,
ByVal e As System.EventArgs) Handles MyBase.Load
Dim z As TreeNode z = TreeView1.Nodes.Add("c:\") Explor(z) z.ImageIndex = 1 z.SelectedImageIndex = 0 End Sub C'est tout de suite plus classieux, hein ?
A proprement parler, et pour qui a été élevé dans les canons de l'algorithmique rigoriste, les lignes du genre : z = Node.Nodes.Add(Path.GetFileName(s)) sont une pure profanation. En effet, elles impliquent qu'on puisse à la fois appliquer une méthode (Add en l'occurrence) et affecter une variable, le tout d'un même élan. VB.Net autorise donc ce genre d'écriture hérétique, de même qu'il permet, nous en avons déjà parlé, d'affecter une variable tout en la déclarant On peut même trouver sous la plume de certains programmeurs des lignes proprement sataniques, qui consistent tout à la fois à déclarer une variable, à l'affecter et à appliquer une méthode : Dim z as TreeNode = Node.Nodes.Add(Path.GetfileName(s)) Alors, ce type d'écriture est un peu comme les voitures qui roulent à 250 km/h : c'est en vente libre, et c'est bien pratique pour gagner du temps, mais si on veut rester en vie, on n'est pas obligé de les acheter, et encore moins de s'en servir. A bon entendeur... |
|||