TOC

This article is currently in the process of being translated into Italian (~98% done).

Il controllo TreeView:

TreeView - Selection/Expansion state

Nei precedenti articoli, abbiamo usato l'associazione dati per visualizzare oggetti personalizzati in un TreeView WPF. Funziona davvero bene, ma lascia apero un problema: siccome ogni nodo dell'albero è rappresentato dalla classe personalizzata, esempio FamilyMember, non hai più il controllo diretto sulla funzionalità specifica (stato di selezione ed espansione) del nodo TreeView . Nella pratica ciò significa che non è possibile selezionare o espandere/comprimere un determinato nodo da code-behind.

Esistono molte soluzioni per gestirlo. Negli "hack" si utilizzano generatori di elementi di TreeView per ottenere l'elemento TreeViewItem sottostante e si controllano le proprietà IsExpanded e IsSelected. Esistono anche implementazioni molto più avanzate ispirate a MVVM. In questo articolo vorrei mostrarti una soluzione che si trova nel mezzo, facilitando l'implementazione e l'utilizzo, pur non essendo un hack completo.

Una soluzione per selezionare/espandere un TreeView

Il principio di base è implementare due proprietà extra sulla classe di dati: IsExpanded e IsSelected. Queste due proprietà vengono quindi collegate al TreeView, utilizzando un paio di stili destinati a TreeViewItem, all'interno di ItemContainerStyle per TreeView.

Potresti facilmente implementare queste due proprietà su tutti i tuoi oggetti, ma è molto più facile ereditarle da un oggetto base. Se ciò non è possibile per la tua soluzione, puoi creare un'interfaccia e quindi implementarla. Per questo esempio, ho scelto il metodo della classe di base, perché mi consente di ottenere facilmente la stessa funzionalità per i miei oggetti. Ecco il codice:

<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));
		}
	}
}

Mi dispiace per la quantità di codice in un unico posto. In una soluzione del mondo reale, sarebbe ovviamente distribuita su più file e probabilmente i dati per l'albero verrebbero da un'origine dati effettiva, anziché essere generati al volo. Permettimi di spiegare cosa succede nell'esempio.

Parte XAML

Ho definito un paio di pulsanti posizionati nella parte inferiore della finestra di dialogo, per utilizzare le due nuove proprietà. Quindi abbiamo TreeView, per il quale ho definito un ItemTemplate (come dimostrato in un capitolo precedente) nonché un ItemContainerStyle. Se non hai ancora letto i capitoli sullo styling, tu potresti non comprendere completamente quella parte, ma serve semplicemente per collegare le proprietà della nostra classe personalizzata con le proprietà IsSelected e IsExpanded su TreeViewItems, fatto con un Setter di stile. Puoi saperne di più altrove in questo tutorial.

Parte Code-Behind

Nel code-behind, ho definito la classe Person , con un paio di proprietà, che eredita le proprietà extra dalla classe TreeViewItemBase . È necessario tenere presente che la classe TreeViewItemBase implementa l'interfaccia INotifyPropertyChanged e la utilizza per la notifica delle modifiche rilevate su queste due proprietà essenziali - senza questo meccanismo, le modifiche alla selezione / espansione non si rifletteranno nell'interfaccia utente. Il concetto di notifica delle modifiche sono spiegate nei capitoli sulla associazione dei dati.

Nella classe principale di Window, creo semplicemente una serie di persone, aggiungendo bambini ad alcune di esse. Aggiungo le persone a un elenco, che assegno come ItemsSource di TreeView. Con un po' di aiuto da parte del modello definito, le renderizza (vedi screenshot).

La parte più interessante si verifica quando imposto le proprietà IsExpanded e IsSelected sull'oggetto person2 . Questo è ciò che permette alla seconda persona (Jane Doe) di essere contemporaneamente selezionata ed espansa, come mostrato nello screenshot. Usiamo queste due proprietà sugli oggetti Person (ereditati da TreeViewItemBase class) nei gestori di eventi per i due pulsanti di test (tenere presente che, per mantenere il codice il più semplice possibile, il pulsante di selezione funziona solo per gli elementi di livello superiore).

Summary

Per potere lavorare semplicemente con selezioni ed espansioni è necessario: - creare e implementare una classe base per gli oggetti che si desidera utilizzare - manipolare all'interno di un TreeView - utilizzare le proprietà acquisite in ItemContainerStyle. Esistono altre soluzioni per affrontare questo problema e potresti essere in grado di trovare una soluzione che si adatti meglio alle tue esigenze. Come sempre con la programmazione, si tratta di utilizzare lo strumento giusto per il lavoro da svolgere.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!