This article is currently in the process of being translated into French (~99% done).
Lazy loading TreeView items
La manière habituelle de l'utilisation du TreeView consiste à la Binder avec une collection d'éléments ou à ajouter manuellement chaque niveau. Cependant, dans certaines situations, vous souhaitez retarder le chargement des éléments enfants d'un nœud jusqu'à ce que vous en ayez vraiment besoin. Cela est particulièrement utile si vous avez une arborescence très profonde, avec beaucoup de niveaux et de nœuds enfants. Un excellent exemple de cela est la structure de dossiers de votre ordinateur Windows.
Chaque lecteur de votre ordinateur Windows comporte une série de dossiers enfant, et chacun de ces dossiers enfant comporte des dossiers enfant en dessous, etc. Boucler sur chaque lecteur et chaque dossier enfant de lecteur peut prendre beaucoup de temps et votre arborescence sera bientôt constituée de nombreux nœuds, dont un pourcentage élevé ne sera jamais utilisé. C'est la tâche parfaite pour un TreeView "Lazy-Loaded", où les dossiers enfants ne sont chargés qu'à la demande.
Pour faire cela, nous ajoutons simplement un dossier fictif à chaque lecteur ou dossier enfant, puis lorsque l'utilisateur le développe, nous supprimons le dossier fictif et le remplaçons par les valeurs réelles. Voici à quoi ressemble notre application lorsqu'elle démarre - à ce moment-là, nous n'avons obtenu qu'une liste des lecteurs disponibles sur l'ordinateur :
Vous pouvez maintenant commencer à développer les nœuds, et l'application chargera automatiquement les sous-dossiers. Si un dossier est vide, il apparaîtra comme tel lorsque vous essaierez de le développer, comme on peut le voir sur la capture d'écran suivante :
Alors, comment y sommes nous parvenus ? Regardons le code :
<Window x:Class="WpfTutorialSamples.TreeView_control.LazyLoadingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LazyLoadingSample" Height="300" Width="300">
<Grid>
<TreeView Name="trvStructure" TreeViewItem.Expanded="TreeViewItem_Expanded" Margin="10" />
</Grid>
</Window>
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
namespace WpfTutorialSamples.TreeView_control
{
public partial class LazyLoadingSample : Window
{
public LazyLoadingSample()
{
InitializeComponent();
DriveInfo[] drives = DriveInfo.GetDrives();
foreach(DriveInfo driveInfo in drives)
trvStructure.Items.Add(CreateTreeItem(driveInfo));
}
public void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = e.Source as TreeViewItem;
if((item.Items.Count == 1) && (item.Items[0] is string))
{
item.Items.Clear();
DirectoryInfo expandedDir = null;
if(item.Tag is DriveInfo)
expandedDir = (item.Tag as DriveInfo).RootDirectory;
if(item.Tag is DirectoryInfo)
expandedDir = (item.Tag as DirectoryInfo);
try
{
foreach(DirectoryInfo subDir in expandedDir.GetDirectories())
item.Items.Add(CreateTreeItem(subDir));
}
catch { }
}
}
private TreeViewItem CreateTreeItem(object o)
{
TreeViewItem item = new TreeViewItem();
item.Header = o.ToString();
item.Tag = o;
item.Items.Add("Loading...");
return item;
}
}
}
Le code XAML est très simple et un seul détail intéressant est présent : La façon dont nous souscrivons à l'événement "Expanded" du TreeViewItem. Remarquez qu'il s'agit bien du TreeViewItem et non du TreeView lui-même, mais comme l'événement s'affiche dans une bulle, nous pouvons le capturer à un seul endroit pour l'ensemble du TreeView, au lieu de devoir y souscrire pour chaque élément que nous ajoutons à l'arbre. Cet événement est appelé à chaque fois qu'un élément est développé, ce dont nous devons être conscients pour charger ses éléments enfants à la demande.
Dans le Code-behind, nous commençons par ajouter chaque lecteur trouvé sur l'ordinateur au controle TreeView. Nous assignons l'instance DriveInfo à la propriété Tag, afin de pouvoir la récupérer ultérieurement. Notez que nous utilisons une méthode personnalisée pour créer le TreeViewItem, appelée CreateTreeItem(), puisque nous pouvons utiliser exactement la même méthode lorsque nous voulons ajouter dynamiquement un dossier enfant plus tard. Remarquez dans cette méthode comment nous ajoutons un élément enfant à la collection Items, sous la forme d'une chaîne de caractères avec le texte "Chargement..." ("Loading...").
Ensuite nous avons l'événement TreeViewItem_Expanded. Comme nous l'avons déjà mentionné, cet événement est déclenché à chaque fois qu'un élément de l'arborescence est développé. La première chose que nous faisons est donc de vérifier si cet élément a déjà été chargé, en vérifiant si les éléments enfants ne consistent actuellement qu'en un seul élément, qui est une chaîne de caractères - si c'est le cas, nous avons trouvé l'élément enfant "Loading...", ce qui signifie que nous devons maintenant charger le contenu réel et remplacer la place de l'élément réservé par celui-ci.
Nous utilisons maintenant la propriété Tag de l'élément (item) pour obtenir une référence à l'instance DriveInfo ou DirectoryInfo que l'élément courant représente, puis nous obtenons une liste de répertoires enfants, que nous ajoutons à l'élément cliqué, encore une fois en utilisant la méthode CreateTreeItem(). Notez que la boucle où nous ajoutons chaque répertoire enfant se trouve dans un bloc try..catch; ceci est important, car certains chemins peuvent ne pas être accessibles, généralement pour des raisons de sécurité. Vous pouvez saisir l'exception et l'utiliser pour refléter cela dans l'interface d'une manière ou d'une autre.
Résumé
En s'inscrivant à l'événement Expanded, on peut facilement créer un TreeView chargé paresseusement (lazy-loaded), ce qui peut être une bien meilleure solution qu'une solution créée statiquement dans plusieurs situations.