תשובה Nested HierarchicalDataTemplate for leaf and node collection

  • יום שישי 13 אפריל 2012 01:40
     
      קוד כלול

    Hi,

    I have an XML file containing node and leaf node and I want to display them in a treeview. But I have some difficulties understanding how the HierarchicalDataTemplate/DataTemplate works.

    First this is my XML file :

    <?xml version="1.0" encoding="utf-8" ?>
    <root>
    	<node name="Event">
    		<leaf name="Alarm"></leaf>
    		<leaf name="Historic"></leaf>
    		<leaf name="Configuration"></leaf>
    	</node>
    
    	<node name="System">
    		<node name="Configuration">
    			<leaf name="Base configuration"></leaf>
    			<leaf name="Advance configuration"></leaf>
    		</node>
    		<node name="State">
    			<leaf name="Initial state"></leaf>
    			<leaf name="Shutdown state"></leaf>
    		</node>
    	</node>
    </root>

    From that I create a SampleDataSource that I add to my document :

    <SampleData:root x:Key="XMLMyDataSampleDataSource" d:IsDataSource="True"/>

    That's what I've done so far to display in the treeview :

    <HierarchicalDataTemplate x:Key="leafTemplate">
    	<StackPanel>
    		<TextBlock Text="{Binding name}"/>
    	</StackPanel>
    </HierarchicalDataTemplate>		
    
    <HierarchicalDataTemplate x:Key="nodeTemplate" ItemsSource="{Binding leafCollection}" ItemTemplate="{StaticResource leafTemplate}">
    	<StackPanel>
    		<TextBlock Text="{Binding name}"/>
    	</StackPanel>
    </HierarchicalDataTemplate>
    
    <HierarchicalDataTemplate x:Key="menuTemplate" ItemsSource="{Binding nodeCollection}" ItemTemplate="{StaticResource leafTemplate}">
    	<StackPanel>
    		<TextBlock Text="{Binding name}"/>
    	</StackPanel>
    </HierarchicalDataTemplate>
    
    
    ...
    <Grid DataContext="{Binding Source={StaticResource XMLMyDataSampleDataSource}}">
    	<TreeView ItemsSource="{Binding nodeCollection}" ItemTemplate="{StaticResource menuTemplate}" ClipToBounds="True" DataContext="{Binding}"/>
    </Grid>
    

    But this is not giving the correct representation of the XML tree. So far I havetried many combinations of HierarchicalDataTemplate/DataTemplate,but every timesomething is missing.

    I've already done it using an XMLDataSource but as I'm leaning WPF I would like to done it using a SampleData.

    Does anyone have an idea on how to do this properly ? I put many hours trying to figure out, but in vain! :(

    Best regards,


        

כל התגובות

  • יום שישי 13 אפריל 2012 14:09
     
      קוד כלול

    Hi,

    Here's another way I tried, but it still does not work :(

    <!-- For each element of type "node" that has a collection of sub node -->
    <HierarchicalDataTemplate DataType="{x:Type SampleData:node}" ItemsSource="{Binding nodeCollection}">
        <HierarchicalDataTemplate.ItemTemplate>
            <!-- For each sub-element of type "node" *** PROBLEM *** -->
            <DataTemplate DataType="{x:Type SampleData:node}">
                <TextBlock Text="{Binding Path=name}" />    
            </DataTemplate>
        </HierarchicalDataTemplate.ItemTemplate>
        <TextBlock Text="{Binding Path=name}" />
    </HierarchicalDataTemplate>
    
    <!-- For each element of type "leaf" -->
    <DataTemplate DataType="{x:Type SampleData:leaf}" >
        <TextBlock Text="{Binding Path=name}" />
    </DataTemplate>

    Butaccording to logic,it should work ?

    But I thinkmy mistake is probably inlineorwhatis written ***PROBLEM***. Itwould have to beanotherHiearchicalDataTemplateinstead ofItemTemplate.But I'm not sure...

    So here is another try :

    <!-- For each element of type "node" that has a collection of sub node -->
    <HierarchicalDataTemplate DataType="{x:Type SampleData:node}" ItemsSource="{Binding nodeCollection}">
        <HierarchicalDataTemplate.ItemTemplate>
            <!-- For each sub-element of type "node" having sub element of type leafCollection -->
            <HierarchicalDataTemplate DataType="{x:Type SampleData:node}" ItemsSource="{Binding leafCollection}">
                <TextBlock Text="{Binding Path=name}" />
            </HierarchicalDataTemplate>
        </HierarchicalDataTemplate.ItemTemplate>
        <TextBlock Text="{Binding Path=name}" />
    </HierarchicalDataTemplate>
    
    <!-- For each element of type "leaf" -->
    <DataTemplate DataType="{x:Type SampleData:leaf}" >
        <TextBlock Text="{Binding Path=name}" />
    </DataTemplate>
    

    Wow now I'm prety close but this is not working yet. The first level node having only leaf item are not displayed...
     
    • נערך על-ידי Erakis יום שישי 13 אפריל 2012 14:13
    • נערך על-ידי Erakis יום שישי 13 אפריל 2012 14:14
    •  
  • יום שישי 13 אפריל 2012 17:12
     
      קוד כלול
    > Nested HierarchicalDataTemplate for leaf and node collection
     
     
    try using the following example
     
    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <TreeView ItemsSource="{Binding Elements[node]}" ItemTemplate="{DynamicResource node}">
            <TreeView.Resources>
                <Style TargetType="TreeViewItem">
                    <Setter Property="IsExpanded" Value="True" />
                </Style>
                <local:NodeTemplateSelector x:Key="nts" />
                <DataTemplate x:Key="leaf">
                    <TextBlock Text="{Binding Attribute[name].Value, StringFormat='Leaf: {0}'}" />
                </DataTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Elements}" x:Key="node" 
                                          ItemTemplateSelector="{StaticResource nts}">
                    <TextBlock Text="{Binding Attribute[name].Value, StringFormat='Node: {0}'}" />
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Window>
    using System.Windows;
    using System.Windows.Controls;
    using System.Xml.Linq;
    
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                var xe = XElement.Load("..\\..\\XmlFile1.xml");
                this.DataContext = xe;
            }
        }
        public class NodeTemplateSelector : DataTemplateSelector
        {
            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                var xe = item as XElement;
                if (xe == null) return null;
                return (container as FrameworkElement).FindResource(xe.Name.LocalName) as DataTemplate;
            }
        }
    }
      
     
  • יום שני 16 אפריל 2012 12:00
     
     תשובה קוד כלול

    Wow thank Malobukv.

    But last week I finally find a way to solution this problem. But it seem to be the same solution as your. I've not had time to post the answer here before the weekend: (

    <local:MenuItemTemplateSelector x:Key="menuItemTemplateSelector"/>

    <DataTemplate x:Key="leafTemplate" DataType="{x:Type SampleData:leaf}"> <StackPanel> <TextBlock Text="{Binding name}"/> </StackPanel> </DataTemplate> <HierarchicalDataTemplate x:Key="nodeWithSubNodeTemplate" DataType="{x:Type SampleData:node}" ItemsSource="{Binding nodeCollection}"> <StackPanel> <TextBlock Text="{Binding name}"/> </StackPanel> </HierarchicalDataTemplate> <HierarchicalDataTemplate x:Key="nodeWithLeafTemplate" DataType="{x:Type SampleData:node}" ItemsSource="{Binding leafCollection}"> <StackPanel> <TextBlock Text="{Binding name}"/> </StackPanel> </HierarchicalDataTemplate> ... <Grid DataContext="{Binding Source={StaticResource XMLMenuOptionsSampleDataSource}}" ClipToBounds="True" DockPanel.Dock="Bottom" Height="240"> <TreeView ItemsSource="{Binding nodeCollection}" ClipToBounds="True" DataContext="{Binding}" ItemTemplateSelector="{StaticResource menuItemTemplateSelector}"> </TreeView> </Grid>

    And there is the TemplateSelector

    public class MenuItemTemplateSelector : DataTemplateSelector
    {
    	public override DataTemplate SelectTemplate(object item, DependencyObject container)
    	{
    		Window window = Application.Current.MainWindow;
    		if (item is Expression.Blend.SampleData.XMLMenuOptionsSampleDataSource.node)
    		{
    			Expression.Blend.SampleData.XMLMenuOptionsSampleDataSource.node n = ((Expression.Blend.SampleData.XMLMenuOptionsSampleDataSource.node)item);
    			if (n.nodeCollection.Count > 0)
    			{
    				return (DataTemplate)window.FindResource("nodeWithSubNodeTemplate");
    			}
    			else if (n.leafCollection.Count > 0)
    			{
    				return (DataTemplate)window.FindResource("nodeWithLeafTemplate");
    			}
    		}
    		return (DataTemplate)window.FindResource("leafTemplate");
    	}
    }


    I finally realized it was impossible using just a XAML declaration. Since this would require two HierarchicalDataTemplate. Two with the same DataType, but with different ItemSource. Ex :

    <DataTemplate x:Key="Leaf">
    	<StackPanel>
    		<TextBlock Text="{Binding name}"/>
    	</StackPanel>
    </DataTemplate>
    
    <HierarchicalDataTemplate DataType="{x:Type SampleData:node}" ItemsSource="{Binding nodeCollection}" >
    	<StackPanel>
    		<TextBlock Text="{Binding name}"/>
    	</StackPanel>
    </HierarchicalDataTemplate>
    
    <HierarchicalDataTemplate DataType="{x:Type SampleData:node}" ItemsSource="{Binding leafCollection}" >
    	<StackPanel>
    		<TextBlock Text="{Binding name}"/>
    	</StackPanel>
    </HierarchicalDataTemplate>

    But having two HierarchicalDataTemplate having the same DataType and no Key defined is not authorized...

    Anyway, thanks to all for your helps :)
    Best regards
    • נערך על-ידי Erakis יום שני 16 אפריל 2012 12:02
    • סומן כתשובה על-ידי Sheldon _XiaoModerator יום שלישי 01 מאי 2012 07:34
    •  
  • יום שני 16 אפריל 2012 12:11
     
      קוד כלול

    But your solution intrigues me....

    Why using  ItemsSource="{Binding Elements}"  ? What is Elements exactly ? Using this strategy avoiding having to declare two HierarchicalDataTemplate as my solution ?

    I don't understand because I'm not treating my binding source as XML but as a collection. And I don't want too.

    Previously, I was treating my collection in XML and I was able to do that in XAML. Here's how I did it:

    <XmlDataProvider x:Key="XMLMenuOptions"
    			 Source="XMLMenuOptions.xml"
    			 XPath="/root">
    </XmlDataProvider>
    
    <HierarchicalDataTemplate DataType="node"
    					  ItemsSource="{Binding XPath=./*}">
    	<StackPanel Orientation="Horizontal">
    		<TextBlock FontWeight="Bold" Margin="5,0,0,0" Text="{Binding XPath=@name}"></TextBlock>
    	</StackPanel>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="leaf">
    	<StackPanel Orientation="Horizontal">
    		<TextBlock Margin="5,0,0,0" FontWeight="Normal" Text="{Binding XPath=@name}"></TextBlock>
    	</StackPanel>
    </HierarchicalDataTemplate>
    

  • יום שלישי 17 אפריל 2012 05:56
    מנחה דיון
     
     

    Hi Erakis,

    If you have resolved your issue?

    best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.