WPF笔记汇总之TreeView控件

WPF控件之TreeView控件

紧接上一篇《WPF笔记汇总之ListView控件》,这篇主要总结WPF中最复杂的控件之一,TreeView控件,TreeView控件使你能够展示带有层级的数据,每一个数据由树中的一个节点代表。每一个节点可以拥有子节点,子节点也可以拥有自己的子节点。

1. 简单用法

<!--简单使用-->
<TreeView>
	<TreeViewItem Header="Level 1" IsExpanded="True">
		<TreeViewItem Header="Level 2.1" />
		<TreeViewItem Header="Level 2.2" IsExpanded="True">
			<TreeViewItem Header="Level 3.1" />
			<TreeViewItem Header="Level 3.2" />
		</TreeViewItem>
		<TreeViewItem Header="Level 2.3" />
	</TreeViewItem>
</TreeView>
<!--Header图片扩展-->
<TreeView>
	<TreeViewItem IsExpanded="True">
		<TreeViewItem.Header>
			<StackPanel Orientation="Horizontal">
				<Image Source="/images/file.png" />
				<TextBlock Text="Level 1 (Blue)" />
			</StackPanel>
		</TreeViewItem.Header>
		<TreeViewItem>
			<TreeViewItem.Header>
				<StackPanel Orientation="Horizontal">
					<TextBlock Text="Level 2.1" Foreground="Blue" />
				</StackPanel>
			</TreeViewItem.Header>
		</TreeViewItem>
		<TreeViewItem IsExpanded="True">
			<TreeViewItem.Header>
				<StackPanel Orientation="Horizontal">
					<Image Source="/images/file.png" />
					<TextBlock Text="Level 2.2 (Green)" Foreground="Blue" />
				</StackPanel>
			</TreeViewItem.Header>
			<TreeViewItem>
				<TreeViewItem.Header>
					<TextBlock Text="Level 3.1" Foreground="Green" />
				</TreeViewItem.Header>
			</TreeViewItem>
			<TreeViewItem>
				<TreeViewItem.Header>
					<TextBlock Text="Level 3.2" Foreground="Green" />
				</TreeViewItem.Header>
			</TreeViewItem>
		</TreeViewItem>
		<TreeViewItem>
			<TreeViewItem.Header>
				<TextBlock Text="Level 2.3" Foreground="Blue" />
			</TreeViewItem.Header>
		</TreeViewItem>
	</TreeViewItem>
</TreeView>

2. 数据绑定

  • 简单例子
<TreeView Name="trvMenu">
	<TreeView.ItemTemplate>
		<HierarchicalDataTemplate DataType="{x:Type MenuItem}" ItemsSource="{Binding Items}">
			<TextBlock Text="{Binding Title}" />
		</HierarchicalDataTemplate>
	</TreeView.ItemTemplate>
</TreeView>
public MainWindow()
{
	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; }
}

  • 多模板绑定

模板分成分层模板(hierarchical template)和数据模板(DataTemplate),绑定的类型没有任何子成员时用普通的数据模板。

<TreeView Name="trvFamilies">
	<TreeView.Resources>
		<HierarchicalDataTemplate DataType="{x:Type local:Family}" ItemsSource="{Binding Members}">
			<StackPanel Orientation="Horizontal">
				<Image Source="/images/user1.png" />
				<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 local:FamilyMember}">
			<StackPanel Orientation="Horizontal">
				<Image Source="/images/user2.png"/>
				<TextBlock Text="{Binding Name}" />
				<TextBlock Text=" (" Foreground="Green" />
				<TextBlock Text="{Binding Age}" Foreground="Green" />
				<TextBlock Text=" years)" Foreground="Green" />
			</StackPanel>
		</DataTemplate>
	</TreeView.Resources>
</TreeView>
public MainWindow()
{
	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; }
}
  • 选中/展开
    基本原则是在数据类上实现两个额外的属性:IsExpanded和IsSelected。然后将这两个属性连接到TreeView,在TreeView的ItemContainerStyle中使用几个针对TreeViewItem的样式。
<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="/images/user1.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>

定义一个Person类,它有几个属性,它们从TreeViewItemBase类继承了额外的属性。TreeViewItemBase类实现了INotifyPropertyChanged接口,来通知这两个基本属性的更改。

public MainWindow()
{
	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));
	}
}

3. 延迟加载项

如果有一个非常深的树具有许多级别和子节点,则需要使用延迟加载来获取更好的用户体验。子项目仅在需要的时候被加载。

<TreeView Name="trvStructure" TreeViewItem.Expanded="TreeViewItem_Expanded" Margin="10" />
public MainWindow()
{
	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;
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值