第一步,当然是定义要绑定的数据类型了.
在目录树这个例子中,每个TreeViewItem要显示的数据可以用System.IO.DirectoryInfo来表示,但是这样做有一个麻烦:DirectoryInfo只能通过GetDirectories()方法来获取子目录,但是WPF里的数据绑定则更倾向于使用属性在数据间导航,所以为了更方便地使用数据绑定,我们最好还是自定义一个类来完成这样的工作:
using
System.Collections.Generic;
using System.IO;
namespace WpfApplication1
{
class BindDirectory
{
public BindDirectory(string directoryPath)
{
//正规化目录路径,确保Path以'\\'结尾
directoryPath = directoryPath.TrimEnd('\\');
Path = directoryPath + '\\';
//计算出目录名称(不包含路径)
int indexLastSlash = directoryPath.LastIndexOf('\\');
if (indexLastSlash >= 0)
{
Name = directoryPath.Substring(indexLastSlash + 1);
}
else
{
Name = directoryPath;
}
}
public string Name
{
get;
private set;
}
public string Path
{
get;
private set;
}
public IEnumerable<BindDirectory> Directories
{
get
{
//延迟加载
if (directories == null)
{
directories = new List<BindDirectory>();
foreach (string d in Directory.GetDirectories(Path))
{
directories.Add(new BindDirectory(d));
}
}
return directories;
}
}
List<BindDirectory> directories;
}
}
using System.IO;
namespace WpfApplication1
{
class BindDirectory
{
public BindDirectory(string directoryPath)
{
//正规化目录路径,确保Path以'\\'结尾
directoryPath = directoryPath.TrimEnd('\\');
Path = directoryPath + '\\';
//计算出目录名称(不包含路径)
int indexLastSlash = directoryPath.LastIndexOf('\\');
if (indexLastSlash >= 0)
{
Name = directoryPath.Substring(indexLastSlash + 1);
}
else
{
Name = directoryPath;
}
}
public string Name
{
get;
private set;
}
public string Path
{
get;
private set;
}
public IEnumerable<BindDirectory> Directories
{
get
{
//延迟加载
if (directories == null)
{
directories = new List<BindDirectory>();
foreach (string d in Directory.GetDirectories(Path))
{
directories.Add(new BindDirectory(d));
}
}
return directories;
}
}
List<BindDirectory> directories;
}
}
这个类所作的工作很简单,就是正规化目录路径,获取目录名称,以及延迟加载子目录(以提升性能)的列表,我们的界面也只要求它具有这些功能就行了.
第二步,创建一个数据提供类(DataProvider)
我们可以在Window的代码里设置界面的DataContext,ItemsSource等属性来让界面显示指定的数据,也可以构造一个专门提供数据的类,完全在界面(XAML)里指定,这里使用的是第二种方法:
using
System.Collections.Generic;
using System.IO;
namespace WpfApplication1
{
class BindDirectoryList : List<BindDirectory>
{
public BindDirectoryList()
{
foreach (var drive in DriveInfo.GetDrives())
{
Add(new BindDirectory(drive.RootDirectory.FullName));
}
}
}
}
using System.IO;
namespace WpfApplication1
{
class BindDirectoryList : List<BindDirectory>
{
public BindDirectoryList()
{
foreach (var drive in DriveInfo.GetDrives())
{
Add(new BindDirectory(drive.RootDirectory.FullName));
}
}
}
}
这个类就更简单了,仅仅是在创建的时候加载所有的磁盘的根目录.
第三步,设计用户界面
只需要在Window中添加一个TreeView,然后修改几行代码,就能轻松地显示我们的数据了:
<!--
xml:sample这一行用来引入我们自己代码的命名空间
-->
< Window x:Class ="WpfApplication1.Window1"
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sample ="clr-namespace:WpfApplication1"
Title ="Window1" Height ="300" Width ="300" >
< Window.Resources >
<!-- 引入我们自己的数据提供对象 -->
< ObjectDataProvider x:Key ="drives" ObjectType =" {x:Type sample:BindDirectoryList} " />
<!-- 设置如何显示数据,以及如何获取下一级数据的列表 -->
< HierarchicalDataTemplate x:Key ="itemTemplate" DataType =" {x:Type sample:BindDirectory} " ItemsSource =" {Binding Directories} " >
< TextBlock Text =" {Binding Name} " />
</ HierarchicalDataTemplate >
</ Window.Resources >
< TreeView ItemsSource =" {Binding Source={StaticResource drives}} "
ItemTemplate =" {StaticResource itemTemplate} " >
</ TreeView >
</ Window >
这里我们在XAML里定义了一个drives对象,它的类型为BindDirectoryList,创建时会自动加载磁盘的根目录;
< Window x:Class ="WpfApplication1.Window1"
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sample ="clr-namespace:WpfApplication1"
Title ="Window1" Height ="300" Width ="300" >
< Window.Resources >
<!-- 引入我们自己的数据提供对象 -->
< ObjectDataProvider x:Key ="drives" ObjectType =" {x:Type sample:BindDirectoryList} " />
<!-- 设置如何显示数据,以及如何获取下一级数据的列表 -->
< HierarchicalDataTemplate x:Key ="itemTemplate" DataType =" {x:Type sample:BindDirectory} " ItemsSource =" {Binding Directories} " >
< TextBlock Text =" {Binding Name} " />
</ HierarchicalDataTemplate >
</ Window.Resources >
< TreeView ItemsSource =" {Binding Source={StaticResource drives}} "
ItemTemplate =" {StaticResource itemTemplate} " >
</ TreeView >
</ Window >
我们还定义了一个针对BindDirectory类型的层次型数据模板itemsTemplate,指定了要获取此类型的数据的子数据需要通过Directories属性,并且告诉WPF用一个TextBlock来显示它的名称.
最后,我们设置一下TreeView的ItemsSource和ItemTemplate就完成工作了.
运行截图: