TreeView 控件时最重要的Windows控件之一,它被集成到Windows 资源管理器乃至 .Net 帮助文档库等各种元素中。WPF中实现的TreeView 控件更是令人印象深刻,因为它完全支持数据绑定。
TreeView 控件在本质上是驻留TreeViewItem 对象的特殊ItemsControl 控件。但与ListViewItem 对象不同,TreeViewItem 对象不是内容控件。相反,每个TreeViewItem 对象都是单独的ItemsControl 控件,可以包含更多TreeViewItem 对象。通过这一灵活性,可以创建更深层次的数据显示。
使用标记声明的TreeView :
可以看到使用起来非常简单,只有几个Header字段,TreeViewItem可以多层次嵌套。
使用数据绑定
这里需要逐层写模板,每层只有一个TextBlock用于显示Header字段。
实际上,现在有两个模板,每个模板用于树控件中的每个层次。第二个模板使用从第一个模板中选择的项作为其数据源。
尽管这个标记工作的很好,单分解每个数据模板并通过数据类型(而不是通过位置)将模板应用到数据对象的情况更加普遍。
TreeView 控件经常用于包含大量数据,这是因为TreeView 控件的显示是能够折叠的。即使用户从顶部滚动到底部,也不需要显示全部信息。完全可以在TreeView 控件中省略不显示的信息,以便降低开销。甚至更好的是,当展开每个TreeViewItem 对象时会引发 Expanded 事件,并且当关闭时会引发 Collapsed 事件。可通过处理这两个事件即时填充丢失的节点或丢弃不需要的节点。这种技术被称为即时创建节点。
private void BuildTree()
{
treeFileSystem.Items.Clear();
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
TreeViewItem item = new TreeViewItem();
item.Tag = drive;
item.Header = drive.ToString();
item.Items.Add("*");
treeFileSystem.Items.Add(item);
}
}
private void treeFileSystem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = (TreeViewItem)e.OriginalSource;
item.Items.Clear();
DirectoryInfo dir;
if (item.Tag is DriveInfo)
{
DriveInfo drive = (DriveInfo)item.Tag;
dir = drive.RootDirectory;
}
else
{
dir = (DirectoryInfo)item.Tag;
}
try
{
foreach (DirectoryInfo subDir in dir.GetDirectories())
{
TreeViewItem newItem = new TreeViewItem();
newItem.Tag = subDir;
newItem.Header = subDir.ToString();
newItem.Items.Add("*");
item.Items.Add(newItem);
}
}
catch
{
// An exception could be thrown in this code if you don't
// have sufficient security permissions for a file or directory.
// You can catch and then ignore this exception.
}
}
完整的测试代码:
MainWindow.xaml
MainWindow.xaml.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System;
using System.Windows;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.IO;
namespace TestTreeView;
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetProperty(ref T member, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer.Default.Equals(member, value))
{
return false;
}
member = value;
OnPropertyChanged(propertyName);
return true;
}
}
public class Order : ViewModelBase
{
private decimal price = 0;
public decimal Price { get => price; set => SetProperty(ref price, value); }
private int volume = 0;
public int Volume { get => volume; set => SetProperty(ref volume, value); }
private DateTime orderDate = DateTime.Now;
public DateTime OrderDate { get => orderDate; set => SetProperty(ref orderDate, value); }
private string image = string.Empty;
public string Image { get => image; set => SetProperty(ref image, value); }
}
public class Account : ViewModelBase
{
private string accountID = string.Empty;
public string AccountID { get => accountID; set => SetProperty(ref accountID, value); }
private ObservableCollection orders = new ObservableCollection();
public ObservableCollection Orders { get => orders; set => SetProperty(ref orders, value); }
}
public class AccountGroup : ViewModelBase
{
private string groupID = string.Empty;
public string GroupID { get => groupID; set => SetProperty(ref groupID, value);}
private ObservableCollection accounts = new ObservableCollection();
public ObservableCollection Accounts { get => accounts; set => SetProperty(ref accounts, value);}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
myGrid.DataContext = this;
InitAccountGroup();
BuildTree();
}
public void InitAccounts()
{
Order order1 = new Order();
Order order2 = new Order();
Order order3 = new Order();
Order order4 = new Order();
order1.Price = 100;
order1.Volume = 10;
order1.Image = "image1.gif";
order2.Price = 1000;
order2.Volume = 100;
order2.Image = "image2.gif";
order3.Price = 10000;
order3.Volume = 1000;
order3.Image = "image3.gif";
order4.Price = 100000;
order4.Volume = 10000;
order4.Image = "image4.gif";
Account account1 = new Account();
Account account2 = new Account();
account1.AccountID = "Account1";
account1.Orders.Add(order1);
account1.Orders.Add(order2);
account2.AccountID = "Account2";
account2.Orders.Add(order3);
account2.Orders.Add(order4);
Accounts.Add(account1);
Accounts.Add(account2);
}
public void InitAccountGroup()
{
InitAccounts();
AccountGroup accountGroup1 = new AccountGroup();
AccountGroup accountGroup2 = new AccountGroup();
accountGroup1.GroupID = "Group1";
accountGroup1.Accounts = Accounts;
accountGroup2.GroupID = "Group2";
accountGroup2.Accounts = Accounts;
AccountGroups.Add(accountGroup1);
AccountGroups.Add(accountGroup2);
}
public ObservableCollection Accounts { get; set; } = new ObservableCollection();
public ObservableCollection AccountGroups { get; set; } = new ObservableCollection();
private void BuildTree()
{
treeFileSystem.Items.Clear();
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
TreeViewItem item = new TreeViewItem();
item.Tag = drive;
item.Header = drive.ToString();
item.Items.Add("*");
treeFileSystem.Items.Add(item);
}
}
private void treeFileSystem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = (TreeViewItem)e.OriginalSource;
item.Items.Clear();
DirectoryInfo dir;
if (item.Tag is DriveInfo)
{
DriveInfo drive = (DriveInfo)item.Tag;
dir = drive.RootDirectory;
}
else
{
dir = (DirectoryInfo)item.Tag;
}
try
{
foreach (DirectoryInfo subDir in dir.GetDirectories())
{
TreeViewItem newItem = new TreeViewItem();
newItem.Tag = subDir;
newItem.Header = subDir.ToString();
newItem.Items.Add("*");
item.Items.Add(newItem);
}
}
catch
{
// An exception could be thrown in this code if you don't
// have sufficient security permissions for a file or directory.
// You can catch and then ignore this exception.
}
}
}