如何将Xml属性绑定到TreeView节点,在将XDocument数据绑定到WPF Treeview时。

4

我有一个XML需要绑定到一个WPF TreeView。这里的XML可能具有不同的结构。TreeView应该是通用的数据绑定,可以加载任何层次结构的排列方式。然而,节点上的XAttribute(称为Title)应该绑定到TreeViewItem的标题文本而不是节点名称。

要绑定的XML:

<Wizard>
  <Section Title="Home">
    <Loop Title="Income Loop">
      <Page Title="Employer Income"/>
      <Page Title="Parttime Job Income"/>
      <Page Title="Self employment Income"/>
    </Loop>
  </Section>
  <Section Title="Deductions">
    <Loop Title="Deductions Loop">
      <Page Title="Travel spending"/>
      <Page Title="Charity spending"/>
      <Page Title="Dependents"/>
    </Loop>
  </Section>
</Wizard>

XAML:

<Window x:Class="Wpf.DataBinding.TreeViewer"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Wpf.DataBinding"
    Title="TreeViewer" Height="300" Width="300">
    <Window.Resources>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Elements}" x:Key="TVTemplate">
            <TreeViewItem Header="{Binding Path=Name}"/>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <StackPanel>
        <TreeView x:Name="_treeView" Style="{StaticResource TVallExpanded}"
                ItemsSource="{Binding Path=Root.Elements}"
                ItemTemplate="{StaticResource TVTemplate}" />
    </StackPanel>
</Window>

使用XAML的代码后台加载XML到XDocument并将其绑定到TreeView

public partial class TreeViewer : Window
{
    public TreeViewer()
    {
        InitializeComponent();
        XDocument doc = XDocument.Parse(File.ReadAllText(@"C:\MyWizard.xml"));
        _treeView.DataContext = doc;
    }
}

因此,在XAML标记中,我们将Name绑定到TreeViewItem的标题。

<TreeViewItem Header="{Binding Path=Name}"/>

然而,我希望将其绑定到上面Xml中的Section、Loop和Page的Title属性。我读到在绑定XDocument时不可能使用XPath。但是一定有一种方法可以将Title属性绑定到TreeViewItem的Header文本中。我尝试使用@Title、.[@Title]等,但似乎都不起作用。
这个MSDN论坛上的讨论与此类似。
任何指针都将非常有帮助。
2个回答

14

太棒了!!! 我找到了如何绑定XAttribute。这不是直观的,也不容易想象。但是下面是如何完成它的方法。

<TreeViewItem Header="{Binding Path=Attribute[Title].Value}"/>

很难想象标题可以直接用在方括号中。

更多信息请参考此 MSDN 链接


1
我认为,它应该是 Attributes 而不是 Attribute... 至少对我来说它不起作用... - binco
做“Binding XPath=@Title”怎么样? - Sarah Vessels

2
我认为你所需要做的就是为XML中的每个节点类型创建一个HierarchicalDataTemplate,将XML加载到XmlDataProvider中,然后将其绑定到TreeView上。TreeView与XmlDataProvider一起工作来绑定数据,在某个地方它们会找出你定义的HDTs,并将它们的DataType与XML中节点的名称匹配。你可能会遇到一些问题,例如XPATH随着不同类型的数据而改变,但如何保持灵活性则是另一个问题。

例如,我有一个小的正则表达式测试应用程序。它包括一个帮助系统,其中基本上列出了树形结构中的所有不同的正则表达式部分:类别和带有描述、工具提示和其他内容的部分。有关这些部分的数据存储在XML数据源中。由于它是静态的,我只需使用应用程序资源创建一个静态资源:

<XmlDataProvider
    x:Key="rxPartData"
    XPath="RegexParts">
    <x:XData>
        <RegexParts
            xmlns="">
            <Category
                Name="Character class"
                ToolTip="Sets of characters used in matching">
                <RegexPart
                    Regex="[%]"
                    Hint="Positive character group"
                    ToolTip="Matches any character in the specified group (replace % with one or more characters)" />
                <!-- yadda -->
            </Category>
        </RegexParts>
    </x:XData>
</XmlDataProvider>

接下来,我为数据中每种节点类型创建分层数据模板(同样都在应用资源中):
<!-- Category data template -->
<HierarchicalDataTemplate
    DataType="Category"
    ItemsSource="{Binding XPath=*}">
    <TextBlock
        Focusable="False"
        Text="{Binding XPath=@Name}"
        ToolTip="{StaticResource CategoryTooltip}"
        ToolTipService.InitialShowDelay="0"
        ToolTipService.ShowDuration="{x:Static sys:Int32.MaxValue}"
        ToolTipService.HasDropShadow="True" />
</HierarchicalDataTemplate>
<!-- RegexPart data template -->
<HierarchicalDataTemplate
    DataType="RegexPart"
    ItemsSource="{Binding XPath=*}">
    <WrapPanel
        Focusable="False"
        ToolTip="{StaticResource RegexPartTooltip}"
        ToolTipService.InitialShowDelay="0"
        ToolTipService.ShowDuration="{x:Static sys:Int32.MaxValue}"
        ToolTipService.HasDropShadow="True">
        <TextBlock
            Text="{Binding XPath=@Regex}" />
        <TextBlock
            Text=" - " />
        <TextBlock
            Text="{Binding XPath=@Hint}" />
    </WrapPanel>
</HierarchicalDataTemplate>

最后,我只是将树绑定到了XmlDataProvider:
<TreeView
  Name="_regexParts"
  DockPanel.Dock="Top"
  SelectedItemChanged="RegexParts_SelectedItemChanged"
  ItemsSource="{Binding Source={StaticResource rxPartData}, XPath=/RegexParts/Category}"
  ToolTip="Click the + to expand a category; click a part to insert it">
</TreeView>

这就是你需要做的全部内容。TreeView和XmlDataProvider将负责查找和使用数据中正确节点的正确HDT。所有工作中最困难的部分是弄清楚绑定的xpath。如果路径不正确,你将得不到树中任何内容,也不会有任何错误提示(在WPF中有提高数据绑定错误报告的方法,但那是另一个问题)。

谢谢Will。你添加了一些非常好的信息。但是在我处理的情况下,我不喜欢XmlDataProvider。虽然我的示例从文件加载XML,但实际上我直接获取XDocument来绑定到TV。不过我已经找到了解决方案。将其发布为答案。 - Vin
从我的示例中可以看出,使用XmlDataProvider可以大大简化您的xaml,因为它允许您将树的所有模板拆分为HDT。您可以在代码后台中将XDocument转换为其中一个,没有问题。但是如果这对您有用,那就更好了。 - user1228
问题在于,我的XDocument应该是TreeView的对象表示。如果我使用XmlDataProvider,它们会变得解耦,你不觉得吗? - Vin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接