-
TreeView控件:

TreeViews 数据绑定和多种模板

WPF TreeView 支持数据绑定(Data Binding),就像其他所有WPF控件一样,但是TreeView却能够天然的支持继承绑定。普通的DataTemplate通常不够高效。因此,我们使用继承数据模板(HierarchicalDataTemplate),它允许我们同时控制节点,以及该节点的子节点。

TreeView 基本数据绑定

在后面的例子中,我将演示使用分层数据模板(HierarchicalDataTemplate)是一件多么简单的工作。

<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewDataBindingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:self="clr-namespace:WpfTutorialSamples.TreeView_control"
        Title="TreeViewDataBindingSample" Height="150" Width="200">
    <Grid Margin="10">
		<TreeView Name="trvMenu">
			<TreeView.ItemTemplate>
				<HierarchicalDataTemplate DataType="{x:Type self:MenuItem}" ItemsSource="{Binding Items}">
					<TextBlock Text="{Binding Title}" />
				</HierarchicalDataTemplate>
			</TreeView.ItemTemplate>
		</TreeView>
	</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.IO;
using System.Collections.ObjectModel;

namespace WpfTutorialSamples.TreeView_control
{
	public partial class TreeViewDataBindingSample : Window
	{
		public TreeViewDataBindingSample()
		{
			InitializeComponent();
			MenuItem root = new MenuItem() { Title = "Menu" };
			MenuItem childItem1 = new MenuItem() { Title = "Child item #1" };
			childItem1.Items.Add(new MenuItem() { Title = "Child item #1.1" });
			childItem1.Items.Add(new MenuItem() { Title = "Child item #1.2" });
			root.Items.Add(childItem1);
			root.Items.Add(new MenuItem() { Title = "Child item #2" });
			trvMenu.Items.Add(root);
		}
	}

	public class MenuItem
	{
		public MenuItem()
		{
			this.Items = new ObservableCollection<MenuItem>();
		}

		public string Title { get; set; }

		public ObservableCollection<MenuItem> Items { get; set; }
	}

}

在XAML结构化文件中,可以看到TreeView的ItemTemplate具有一个HierarchicalDataTemplate。通过设置这个模板的ItemsSource属性,我指示它使用Items属性来查找子元素,并且我在内部定义了一个真正的模板,该模板只包含一个绑定到Title属性的TextBlock。

第一个例子非常简单,以至于我们只是手动添加了TreeView的元素,而不是生成一组对象并且绑定他们。但是,随着操作越来越复杂,使用数据绑定的优势会更加明显。

多个模板对于不同类型数据

下面这个例子,将增加一点复杂度,我想显示出一个家族树以及成员。一个家族使用一种方式来表现,而成员使用另一种方式来表现。为了完成以一点,我将创建两个模板,并且将他们作为资源指定给这棵树(或者这个窗口,或者这个程序,全都看你),然后令TreeView根据数据的类型选择正确的模板

以下是实现代码 - 随后将进行解释:

<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewMultipleTemplatesSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:self="clr-namespace:WpfTutorialSamples.TreeView_control"
        Title="TreeViewMultipleTemplatesSample" Height="200" Width="250">
	<Grid Margin="10">
		<TreeView Name="trvFamilies">
			<TreeView.Resources>
				<HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
					<StackPanel Orientation="Horizontal">
						<Image Source="/WpfTutorialSamples;component/Images/group.png" Margin="0,0,5,0" />
						<TextBlock Text="{Binding Name}" />
						<TextBlock Text=" [" Foreground="Blue" />
						<TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
						<TextBlock Text="]" Foreground="Blue" />
					</StackPanel>
				</HierarchicalDataTemplate>
				<DataTemplate DataType="{x:Type self:FamilyMember}">
					<StackPanel Orientation="Horizontal">
						<Image Source="/WpfTutorialSamples;component/Images/user.png" Margin="0,0,5,0" />
						<TextBlock Text="{Binding Name}" />
						<TextBlock Text=" (" Foreground="Green" />
						<TextBlock Text="{Binding Age}" Foreground="Green" />
						<TextBlock Text=" years)" Foreground="Green" />
					</StackPanel>
				</DataTemplate>
			</TreeView.Resources>
		</TreeView>
	</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Collections.ObjectModel;

namespace WpfTutorialSamples.TreeView_control
{
	public partial class TreeViewMultipleTemplatesSample : Window
	{
		public TreeViewMultipleTemplatesSample()
		{
			InitializeComponent();

			List<Family> families = new List<Family>();

			Family family1 = new Family() { Name = "The Doe's" };
			family1.Members.Add(new FamilyMember() { Name = "John Doe", Age = 42 });
			family1.Members.Add(new FamilyMember() { Name = "Jane Doe", Age = 39 });
			family1.Members.Add(new FamilyMember() { Name = "Sammy Doe", Age = 13 });
			families.Add(family1);

			Family family2 = new Family() { Name = "The Moe's" };
			family2.Members.Add(new FamilyMember() { Name = "Mark Moe", Age = 31 });
			family2.Members.Add(new FamilyMember() { Name = "Norma Moe", Age = 28 });
			families.Add(family2);

			trvFamilies.ItemsSource = families;
		}
	}

	public class Family
	{
		public Family()
		{
			this.Members = new ObservableCollection<FamilyMember>();
		}

		public string Name { get; set; }

		public ObservableCollection<FamilyMember> Members { get; set; }
	}

	public class FamilyMember
	{
		public string Name { get; set; }

		public int Age { get; set; }
	}
}

就像前面说到的,这两个模板被定义为TreeView的资源的一部分,并且允许TreeView根据不同的数据类型选择合适的模板进行展示。展示Family类型的模板被定义为分层模板(hierarchical template),并且使用Members属性显示家庭成员。

FamilyMember数据类型所使用的模板被定义为常规的数据模板(DataTemplate),因为这个类型没有任何子成员。但是,如果我们希望所有FamilyMember都有子成员,并且这些子成员还有子成员,那么就应该使用分层模板(hierarchical template)

在这两种模板中,我们都是用了一个图片表示了该节点是一个家庭还是家庭成员,并且展示了一些感兴趣的数据,比如家庭成员的数量,或者成员的年龄。

在后台代码中,我们只是简单的创建了两个Family的实例,并且使用一组家庭成员填充他们,然后将这些Family加入到一个列表,也就是TreeView的数据源。

小结

使用数据绑定,可以非常个性化TreeView,也可以使用多个模板表示不同的数据,表现可能性是无尽的。