This article has been localized into French by the community.
VueArborescente - Sélection/Expansion
Dans les articles précédents à propos de la vue arborescente, nous avons utilisé le binding de données pour afficher des objets personnalisés dans une vue arborescente WPF. Cela marche très bien mais il reste un problème: comme chaque nœud est représenté par une classe personnalisée, par exemple FamilyMember comme nous l'avons vu dans l'article précédent, vous n'avez plus directement le contrôle sur les fonctionnalités spécifiques du nœud comme la sélection ou l'état étendu ou non. En pratique, cela signifie qu'on peut sélectionner ou développer/réduire un nœud choisi à partir du code-behind.
Beaucoup de solution existent pour gérer cela, depuis des "hacks" pour lesquels on utilise des générateurs d'items de la vue pour accéder à l'item sous-jacent où il est possible de contrôler les propriétés IsExpanded et IsSelected, jusqu'à des codages plus avancés inspirés du MVVM. Dans cet article, je voudrais vous montrer une solution qui se place vers le milieu, rendant l'implémentation et l'utilisation facile tout en étant pas totalement un hack.
Une solution pour sélectionner/étendre une vue arborescente
Le principe de base est d'implémenter 2 propriétés supplémentaires dans la classe: IsExpanded et IsSelected. C'est deux propriétés sont ensuite reliées à la vue en utilisant des styles ciblant le TreeViewItem, à l'intérieur du ItemContainerStyle pour la vue.
On pourrait simplement implémenter ces 2 propriétés pour chacun de nos objets, mais c'est plus simple de les hériter d'un objet de base. Si cela n'est pas faisable pour votre solution, vous pouvez créer une interface et ensuite l'implémenter à la place pour créer une base commune. Pour cet exemple, j'ai choisi la méthode de la classe de base car elle me permets d'obtenir les même fonctionnalités pour mes autres objets simplement. Voici le code:
<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewSelectionExpansionSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TreeViewSelectionExpansionSample" Height="200" Width="300">
<DockPanel Margin="10">
<WrapPanel Margin="0,10,0,0" DockPanel.Dock="Bottom" HorizontalAlignment="Center">
<Button Name="btnSelectNext" Click="btnSelectNext_Click" Width="120">Select next</Button>
<Button Name="btnToggleExpansion" Click="btnToggleExpansion_Click" Width="120" Margin="10,0,0,0">Toggle expansion</Button>
</WrapPanel>
<TreeView Name="trvPersons">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/user.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" Margin="0,0,4,0" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Controls;
namespace WpfTutorialSamples.TreeView_control
{
public partial class TreeViewSelectionExpansionSample : Window
{
public TreeViewSelectionExpansionSample()
{
InitializeComponent();
List<Person> persons = new List<Person>();
Person person1 = new Person() { Name = "John Doe", Age = 42 };
Person person2 = new Person() { Name = "Jane Doe", Age = 39 };
Person child1 = new Person() { Name = "Sammy Doe", Age = 13 };
person1.Children.Add(child1);
person2.Children.Add(child1);
person2.Children.Add(new Person() { Name = "Jenny Moe", Age = 17 });
Person person3 = new Person() { Name = "Becky Toe", Age = 25 };
persons.Add(person1);
persons.Add(person2);
persons.Add(person3);
person2.IsExpanded = true;
person2.IsSelected = true;
trvPersons.ItemsSource = persons;
}
private void btnSelectNext_Click(object sender, RoutedEventArgs e)
{
if(trvPersons.SelectedItem != null)
{
var list = (trvPersons.ItemsSource as List<Person>);
int curIndex = list.IndexOf(trvPersons.SelectedItem as Person);
if(curIndex >= 0)
curIndex++;
if(curIndex >= list.Count)
curIndex = 0;
if(curIndex >= 0)
list[curIndex].IsSelected = true;
}
}
private void btnToggleExpansion_Click(object sender, RoutedEventArgs e)
{
if(trvPersons.SelectedItem != null)
(trvPersons.SelectedItem as Person).IsExpanded = !(trvPersons.SelectedItem as Person).IsExpanded;
}
}
public class Person : TreeViewItemBase
{
public Person()
{
this.Children = new ObservableCollection<Person>();
}
public string Name { get; set; }
public int Age { get; set; }
public ObservableCollection<Person> Children { get; set; }
}
public class TreeViewItemBase : INotifyPropertyChanged
{
private bool isSelected;
public bool IsSelected
{
get { return this.isSelected; }
set
{
if(value != this.isSelected)
{
this.isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
if(value != this.isExpanded)
{
this.isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Je m'excuse pour ce code plutôt long en un seul endroit. Dans une solution dans un monde réel, il serait évidemment réparti sur plusieurs fichiers et les données pour l'arbre viendraient vraisemblablement d'une source de données courante, plutôt que d'être générées sur le champ. Permettez-moi d'expliquer ce qui se passe dans cet exemple.
Partie XAML
J'ai défini quelques boutons à être disposés au bas du dialogue, pour utiliser les deux nouvelles propriétés. Puis nous avons la VueArborescente (TreeView), pour laquelle j'ai défini un ModèleItem (ItemModel) (tel que démontré dans le chapitre précédent) ainsi que un StyleContenuItem (ItemContainerStyle). Si vous n'avez pas déjà lu les chapitres sur le style, il se peut que vous ne compreniez pas cette partie, mais c'est simplement une question de lier ensemble les propriétés sur notre propre classe personnalisée avec les propriétés EstSélectionnée (IsSelected) et EstÉlargie (IsExpanded) sur ItemsVueArborescente (TreeViewItems), ce qui est accompli avec des réglages de style. Vous pouvez en apprendre plus sur eux ailleurs dans ce didacticiel.
Partie Code-arrière
Dans le code-arrière, j'ai défini une classe Personne (Person), avec quelques propriétés, qui héritent nos propriétés supplémentaires de la classe BaseItemVueArborescente (TreeViewItemBase). Vous devriez savoir que la classe BaseItemVueArborescente (TreeViewItemBase) applique l'interface INotifiePropriétéChangée (INotifyPropertyChanged) et l'utilise pour signifier les changements à ces deux propriétés essentielles - sans ceci, les changements sélection/élargissement ne seront pas renvoyés dans l'interface graphique. Le concept de changements en notification est expliqué dans les chapitres sur la Liaison de données.
Dans la classe de fenêtre principale, je crée tout simplement une rangée de personnes, tout en ajoutant des enfants à quelques-unes d'eux. J'ajoute les personnes à une liste, laquelle j'attribue comme une SourceItems (ItemsSource) de l'arbre arborescente, qui, avec un peu d'aide du modèle défini, va les rendre de la façon affichée sur la capture d'écran.
La partie la plus intéressante se produit lorsque j'ajuste les propriétés EstÉlargie et EstSélectionnée sur l'objet personne2 (person2). C'est ceci qui cause la seconde personne (Jane Doe) à être initialement sélectionnée et élargie, tel qu'affiché sur la capture d'écran. Nous utilisons aussi ces deux propriétés sur les deux objets Personne (héritées de la classe BaseItemVueArborescente (TreeViewItemBase) dans les gestionnaires d'événements pour les deux boutons de test (s'il vous plaît ne perdez pas de vue que, pour garder le code aussi petit et simple que possible, le bouton de sélection fonctionne seulement pour les items de niveau supérieur).
Résumé
En créant et en mettant en oeuvre une classe de base pour les objets que vous souhaitez utiliser et manipuler sous la VueArborescente (TreeView), et utilisant les propriétés acquises dans le StyleContenuItem (ItemContainerStyle), vous simplifiez beaucoup le travail avec les sélections et les états d'élargissement. Il existe plusieurs solutions pour s'attaquer à ce problème, et pendant que ceci devrait faire l'affaire, vous pourriez être capable de trouver une solution qui correspond mieux à vos besoins. Comme toujours, avec la programmation, le tout est de trouver le bon outil pour le travail à accomplir.