I'd like to display arbitrary XML in a TreeView, with expanding and collapsing nodes, showing both the element name and the set of attributes and their values. I think I can do this with HierarchicalDataTemplate .
I've seen the hints to use HierarchicalDataTemplate to display arbitrary XML elements, and text nodes, like this:
<Window.Resources>
<HierarchicalDataTemplate x:Key="NodeTemplate">
<TextBlock x:Name="tbName" Text="?" />
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider">
</XmlDataProvider>
</Window.Resources>
....
<TreeView Name="treeView1"
ItemsSource="{Binding Source={StaticResource xmlDataProvider}, XPath=*}"
ItemTemplate= "{StaticResource NodeTemplate}"/>
Which works great. It displays the element names and text for each element. But my XML uses attributes to carry information. The schema is complex and I don't have a formal definition of it, so for now I am treating it as arbitrary XML.
The simplest document looks like this:
<c4soap name="GetVersionInfo" seq="" result="1">
<versions>
<version name="Director"
version="2.1.0.126418"
buildtype=""
builddate="Jun 1 2011" buildtime="14:52:43" />
<version name="MediaManager"
version="2.1.0.126418"
buildtype=""
builddate="Jun 1 2011"
buildtime="14:36:17" />
</versions>
</c4soap>
Using the above HierarchicalDataTemplate definition, I get this for a display:
Not quite what I want. For each node I want to display both the element name and the set of attributes and their values.
I tried this:
<Window.Resources>
<HierarchicalDataTemplate x:Key="NodeTemplate">
<WrapPanel
Focusable="False">
<TextBlock x:Name="tbName" Text="?" />
<TextBlock x:Name="tbAttrs" Text="?" />
</WrapPanel>
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
<Setter TargetName="tbAttrs" Property="Text" Value="{Binding Path=Attributes}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider">
</XmlDataProvider>
</Window.Resources>
... which gets me kinda close, but the
Value="{Binding Path=Attributes}"
results in a display of "(Collection)" in the TreeView.
How can I simply display all the actual attribute names and values, in addition to the element name?
I added an ItemsControl
into the template, like this :
<Window.Resources>
<SolidColorBrush x:Key="xmlValueBrush" Color="Blue" />
<SolidColorBrush x:Key="xmAttributeBrush" Color="Red" />
<SolidColorBrush x:Key="xmlTagBrush" Color="DarkMagenta" />
<SolidColorBrush x:Key="xmlMarkBrush" Color="Blue" />
<DataTemplate x:Key="attributeTemplate">
<StackPanel Orientation="Horizontal"
Margin="3,0,0,0"
HorizontalAlignment="Center">
<TextBlock Text="{Binding Path=Name}"
Foreground="{StaticResource xmAttributeBrush}"/>
<TextBlock Text="=""
Foreground="{StaticResource xmlMarkBrush}"/>
<TextBlock Text="{Binding Path=Value}"
Foreground="{StaticResource xmlValueBrush}"/>
<TextBlock Text="""
Foreground="{StaticResource xmlMarkBrush}"/>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="nodeTemplate">
<StackPanel Orientation="Horizontal"
Focusable="False">
<TextBlock x:Name="tbName" Text="?" />
<ItemsControl
ItemTemplate="{StaticResource attributeTemplate}"
ItemsSource="{Binding Path=Attributes}"
HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider">
</XmlDataProvider>
</Window.Resources>
Now it displays element names and the set of attributes and their values, like this:
you also can use a template selector for the different node types and use the XPath node()|@* to loop thru all types of nodes:
<TreeView
x:Name="TreeView"
ItemsSource="{Binding}"
ItemTemplateSelector="{DynamicResource ResourceKey=NodeTemplateSelector}">
<TreeView.Resources>
<HierarchicalDataTemplate x:Key="TextTemplate">
<Grid>
<uixml:TextInputControl DataContext="{Binding}" />
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="AttributeTemplate">
<Grid>
<uixml:AttributeInputControl DataContext="{Binding}" />
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="NodeTemplate" >
<TextBlock Text="{Binding Path=Name}" />
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()|@*" />
</HierarchicalDataTemplate.ItemsSource>
</HierarchicalDataTemplate>
<ui:XmlTemplateSelector
x:Key="NodeTemplateSelector"
NodeTemplate="{StaticResource NodeTemplate}"
TextTemplate="{StaticResource TextTemplate}"
AttributeTemplate="{StaticResource AttributeTemplate}" />
</TreeView.Resources>
</TreeView>
and :
public class XmlTemplateSelector:DataTemplateSelector{
public DataTemplate NodeTemplate { get; set; }
public DataTemplate TextTemplate { get; set; }
public DataTemplate AttributeTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
XmlNode node = (XmlNode)item;
switch (node.NodeType) {
case XmlNodeType.Attribute:
return AttributeTemplate;
case XmlNodeType.Element:
return NodeTemplate;
case XmlNodeType.Text:
return TextTemplate;
}
throw new NotImplementedException(String.Format("not implemented for type {0}", node.NodeType));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With