TOC

This article has been localized into Portuguese by the community.

O controle TreeView:

TreeView - Seleção e Expansão

Nos dois artigos anteriores do TreeView, usamos ligação de dados para exibir objetos personalizados em um TreeView do WPF. Isso funciona muito bem, mas deixa um problema: como cada nó da árvore agora é representado por sua classe personalizada, por exemplo FamilyMember como vimos no artigo anterior, você não tem mais controle direto sobre a funcionalidade específica do nó TreeView como seleção e estado de expansão. Na praxis, isso significa que você não pode selecionar ou expandir / recolher um determinado nó de code-behind.

Existem várias soluções para lidar com isso, desde "hacks", onde você usa os geradores de itens do TreeView para obter o TreeViewItem subjacente, onde você pode controlar as propriedades IsExpanded e IsSelected, até implementações muito mais avançadas inspiradas no MVVM. Neste artigo, gostaria de mostrar uma solução que está em algum lugar no meio, facilitando a implementação e o uso, embora ainda não seja um hack completo.

Uma solução de seleção / expansão

O princípio básico é implementar duas propriedades extras em sua classe de dados: IsExpanded e IsSelected. Essas duas propriedades são conectadas ao TreeView, usando alguns estilos direcionados ao TreeViewItem, dentro do ItemContainerStyle para o TreeView.

Você pode facilmente implementar essas duas propriedades em todos os seus objetos, mas é muito mais fácil herdá-las de um objeto base. Se isso não for viável para sua solução, você poderá criar uma interface para ela e, em seguida, implementá-la para estabelecer um ponto comum. Para este exemplo, escolhi o método da classe base, porque me permite obter facilmente a mesma funcionalidade para meus outros objetos. Aqui está o código:

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

Sinto muito pela quantidade bastante grande de código em um só lugar. Em uma solução do mundo real, seria obviamente distribuída em vários arquivos, e os dados da árvore provavelmente viriam de uma fonte de dados real, em vez de serem gerados na hora. Permita-me explicar o que acontece no exemplo.

Parte XAML

Eu defini alguns botões para serem colocados na parte inferior da caixa de diálogo, para usar as duas novas propriedades. Em seguida, temos o TreeView, para o qual eu defini um ItemTemplate (como demonstrado em um capítulo anterior), bem como um ItemContainerStyle. Se você ainda não leu os capítulos sobre estilo, talvez não entenda completamente essa parte, mas é simplesmente uma questão de vincular as propriedades em nossa própria classe personalizada com o IsSelected e o IsExpanded propriedades no TreeViewItems, o que é feito com setters de estilo. Você pode aprender mais sobre eles em outro lugar neste tutorial.

Peça por trás de código

No code-behind, eu defini uma classe Person , com algumas propriedades, que herdam nossas propriedades extras da classe TreeViewItemBase . Você deve estar ciente de que a classe TreeViewItemBase implementa a interface INotifyPropertyChanged e a utiliza para notificar sobre alterações nessas duas propriedades essenciais - sem isso, as alterações de seleção / expansão não serão refletidas na interface do usuário. O conceito de alterações de notificação é explicado nos capítulos de vinculação de dados.

Na classe Window principal eu simplesmente crio um grupo de pessoas, enquanto adiciono filhos a algumas delas. Eu adiciono as pessoas a uma lista, que eu atribuo como o ItemsSource do TreeView, que, com um pouco de ajuda do modelo definido, as processa da maneira como são mostradas na captura de tela.

A parte mais interessante acontece quando eu defino as propriedades IsExpanded e IsSelected no objeto person2 . Isso é o que faz com que a segunda pessoa (Jane Doe) seja inicialmente selecionada e expandida, conforme mostrado na captura de tela. Também usamos essas duas propriedades nos objetos Person (herdados da classe TreeViewItemBase) nos manipuladores de eventos dos dois botões de teste (lembre-se de que, para manter o código o mais pequeno e simples possível, o botão de seleção só funciona para os itens de nível superior).

Resumo

Ao criar e implementar uma classe base para os objetos que você deseja usar e manipular dentro de um TreeView, e usando as propriedades obtidas no ItemContainerStyle, você torna muito mais fácil trabalhar com seleções e estados de expansão. Há muitas soluções para resolver esse problema e, embora isso seja uma boa solução, talvez você consiga encontrar uma solução que atenda melhor às suas necessidades. Como sempre com programação, é tudo sobre como usar a ferramenta certa para o trabalho em mãos.


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!