在本文中,将探讨如何使用WPF应用程序中的TreeView控件来显示一个简单XML文件中的数据,而无需编写任何代码。所有的关键操作都在定义窗口内容的XAML文件中完成。虽然可能不会尝试用这种方式编写商业应用程序,但这里可能会有一些知识可以帮助更容易地构建商业应用程序。
本文基于两篇关于使用WPF和XML的文章的信息。第一篇是Josh Smith的《对简单WPF资源管理器树的反馈》,在这篇文章中,Smith展示了如何使用DataTemplate定义TreeViewItem的内容,以及如何使用窗口的“Loaded”事件处理程序构建树结构。第二篇文章是Karl Shifflett的《对简单WPF资源管理器树的过度反应》,Shifflett的文章描述了HierarchicalDataTemplate,展示了它如何帮助递归树结构数据来填充TreeView控件。
在寻找一种解决方案来填充TreeView,显示类似于Windows资源管理器的目录树,包括至少一些文件名时,遇到了TreeView的奇怪行为,并决定使用XML数据进行一些测试。这本身就是一个挑战,但是通过从Shifflett和Smith的文章中获取信息,以及一些思考,找到了下面描述的解决方案,使用XML数据填充TreeView控件。
实际上,使用WPF显示XML数据相当容易。除了XML文件本身,几乎所有的工作都在XAML中完成,大部分是通过定义几个资源来完成的。本文附带的源代码是一个完整的工作示例。项目配置为使用.NET 4.0,但可以将MainWindow.xaml和testdata.xml移动到WPF 3.0项目中,它应该同样有效。
在下面的文本中,将只突出显示使这成为可能的XAML代码的示例。
当创建一个WPF项目时,会在一个名为MainWindow.xaml的文件中创建一个骨架窗口,该文件在Visual Studio编辑器中打开。包括一些XAML来配置默认窗口,使用Grid控件来容纳窗口的其余视觉元素。在代码中,在Window元素的开头,Grid标签之前,添加了一个新的子元素,并在Window标签下方,创建了一个在Window内定义一些资源的部分。
资源部分最初看起来像这样:
<Window.Resources>
</Window.Resources>
然后在这些Window.Resources标签之间,添加了几个将被UI用来显示XML数据的元素。添加的第一个资源是XmlDataProvider。XmlDataProvider是一个类,它使得将XML数据绑定到元素并使用XML结构成为可能。通过像这样创建一个资源来创建一个对象:
<XmlDataProvider x:Key="xmldata" Source="testdata.xml" XPath="/root" />
Source属性设置为为测试创建的XML文件的名称,但实际上在运行时并不引用文件。通过将文件放在项目的根目录中,并将构建操作设置为"Resource",文件的内容实际上在编译时成为程序集的一部分。因此,在这种情况下,文件名成为资源的键名称,WPF足够智能,可以在应用程序运行时计算出细节。XPath属性用于指定一个节点。XML测试文件有一个名为"root"的根节点,所以在这里使用它来开始XML解析。
有趣的工作是在数据模板中完成的,这些模板是使用HierarchicalDataTemplate创建的。再次,在Window.Resources元素内,紧接XmlDataProvider之后,为XML测试文件中使用的每种类型的标签创建一个数据模板。在这种情况下,testdata.xml中存在三个节点项,"root"、"Node"和"leaf"。所以创建了三个数据模板。"Node"的模板看起来像这样:
<HierarchicalDataTemplate DataType="Node" ItemsSource="{Binding XPath=./*}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0" Text="Node:" />
<TextBlock Margin="5,0,0,0" Text="{Binding XPath=@name}" />
</StackPanel>
</HierarchicalDataTemplate>
其他模板类似。需要注意的主要区别是,没有为"leaf"节点指定ItemsSource属性,因为知道没有在"leaf"节点中放置任何项目。这样的节点总是在行的末端,所以没有必要检查它们是否包含内容。ItemsSource属性由HierarchicalDataTemplate特别为此用途提供。
HierarchicalDataTemplate使用ItemsSource来确定当前项是否包含其他项。如果没有,节点将相应地呈现,并且无法进一步导航到节点中。但如果当前项包含其他项,它将被渲染,以便用户可以钻取到节点中查看其他项。
通过设置Grid的DataContext和TreeView控件的ItemsSource属性来绑定XML数据到UI控件。完整的UI定义在几行内完成。
<Grid DataContext="{StaticResource xmldata}">
<TreeView Name="dirTree" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding}" VirtualizingStackPanel.IsVirtualizing="False" VirtualizingStackPanel.VirtualizationMode="Standard" />
</Grid>
就是这样!
HierarchicalDataTemplate类使得使用TreeView填充XML数据变得非常容易。没有理由它不能在其他场景中使用,以帮助更复杂的情况。要记住的主要事情是为TreeView预期的每种类型创建一个模板(或者为父类型创建一个模板,以捕获几种派生类型),并在可能包含更多需要进入TreeView的数据的类型上设置ItemsSource。