Question

I have an XML that needs to be databound to a WPF TreeView. Here the XML can have different structure. The TreeView should be databound generic enough to load any permutation of hierarchy. However an XAttribute on the nodes (called Title) should be databound to the TreeViewItem's header text and not the nodename.

XML to be bound:

<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's codebehind that loads XML to XDocument and binds it to TreeView

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

So in the XAML markup we are binding Name to TreeViewItem's header.

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

However, I want to bind it to Title attribute of Section, Loop and Page in the Xml above. I read that it's not possible to use XPath while binding XDocument. But there has to be a way to bind the Title attribute to TreeViewItem's Header text. I tried using @Title, .[@Title] etc. But none seemed to work.

This thread on MSDN Forums has a similar discussion.

Any pointers would be greatly helpful.

Was it helpful?

Solution

Hurrah !!! I figured out how to bind XAttribute. It is not intuitive and it's not easily imaginable. But here is how it can be done.

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

It is hard to imagine that Title can directly be used in square braces.

More @ this MSDN link

OTHER TIPS

I think all you need to do is create a HierarchicalDataTemplate for each node type in your XML, load your xml it into an XmlDataProvider, and then bind that to the TreeView. The TV works with the XDP to bind data, and somewhere along the line they figure out what HDTs you have defined and match their DataType to the names of the nodes in your XML. You might have some issues with your XPATHs changing with the different types of data, but keeping those flexible is another question.

For example, I have a little regex test app. It includes a help system which is essentially all the different regex parts listed in a tree: Categories and parts with descriptions, tooltips, and other stuff. The data about the parts is stored as an xml data source. Since its static, I just created a static resource with the application's resources:

<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>

Next, I created HierarchicalDataTemplates for each node type in the data (again, all of this is in the application's resources):

<!-- 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>

Lastly, I just bound the tree to the 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>

And that's all you have to do. The TreeView and the XmlDataProvider will take care of finding and using the correct HDT's for the correct nodes in the data. The hardest part of all this is figuring out your xpaths for binding. It can get a little tricky, as if your paths are incorrect, you'll end up getting nothing in the tree and there won't be any errors (there are ways to increase error reporting in databinding in WPF, but that's another question).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top