0

I am trying to use a HierarchicalDataTemplate to recursively create expanders with items in them but when I use the HierarchicalDataTemplate I only get the first level of items displayed.

Please let me know if you need anyore information.

Heres the what the xaml would look like if I was writing by hand:

<GroupBox Header="SectionHeader">
    <StackPanel >
        <Expander VerticalAlignment="Top" Header="SubSectionHeader">
            <StackPanel>
                <Expander VerticalAlignment="Top" Header="SubSectionHeader" Margin="10,0,0,0">
                    <StackPanel>
                        etc......
                    </StackPanel>
                </Expander>
         </Expander>
    </StackPanel>
</GroupBox>

Heres what I have so far.

Xaml:

<ItemsControl Name="lstMain" ItemsSource="{Binding Sections}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <GroupBox Header="{Binding Section.SectionName}">
                <ItemsControl ItemsSource="{Binding SubSections}" ItemTemplate="{StaticResource BinderTemplate}" />
            </GroupBox>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

<HierarchicalDataTemplate x:Key="BinderTemplate" ItemsSource="{Binding Path=SubSections}" DataType="{x:Type local:SubSectionViewModel}">
    <StackPanel>
        <Expander Header="{Binding SubSection.SubSectionName}"/>
    </StackPanel>
</HierarchicalDataTemplate>

Data Classes:

class TopViewModel
{
    ObservableCollection<SectionViewModel> _sections = new ObservableCollection<SectionViewModel>();

    public ObservableCollection<SectionViewModel> Sections
    {
        get
        {
            return _sections;
        }
        set
        {
            _sections = value;
        }
    }
}

public class SectionViewModel
{
    ObservableCollection<MaterialViewModel> _materials = new ObservableCollection<MaterialViewModel>();
    ObservableCollection<SubSectionViewModel> _subSections = new ObservableCollection<SubSectionViewModel>();
    Section _section;

    public Section Section
    {
        get
        {
            return _section;
        }
        set
        {
            _section = value;
        }
    }

    public string MaterialName
    {
        get { return Section.SectionName; }
        set { Section.SectionName = value; }
    }

    public ObservableCollection<MaterialViewModel> Materials
    {
        get
        {
            return _materials;
        }
        set
        {
            _materials = value;
        }
    }

    public ObservableCollection<SubSectionViewModel> SubSections
    {
        get
        {
            return _subSections;
        }
        set
        {
            _subSections = value;
        }
    }
}

public class SubSectionViewModel
{
    ObservableCollection<MaterialViewModel> _materials = new ObservableCollection<MaterialViewModel>();
    ObservableCollection<SubSectionViewModel> _subSections = new ObservableCollection<SubSectionViewModel>();
    SubSection _subSection;

    public ObservableCollection<MaterialViewModel> Materials
    {
        get
        {
            return _materials;
        }
        set
        {
            _materials = value;
        }
    }

    public ObservableCollection<SubSectionViewModel> SubSections
    {
        get
        {
            return _subSections;
        }
        set
        {
            _subSections = value;
        }
    }

    public SubSection SubSection
    {
        get
        {
            return _subSection;
        }
        set
        {
            _subSection = value;
        }
    }
}
WildCrustacean
  • 5,896
  • 2
  • 31
  • 42
user589195
  • 4,180
  • 13
  • 53
  • 81

3 Answers3

3

You are missing a key bit in your HierarchicalDataTemplate - how to render the subelements:

<HierarchicalDataTemplate x:Key="BinderTemplate" 
    ItemsSource="{Binding Path=SubSections}" 
    DataType="{x:Type local:SubSectionViewModel}">
    <StackPanel>
        <Expander Header="{Binding SubSection.SubSectionName}">
            <ItemsControl Margin="5,0,0,0" 
                      ItemsSource="{Binding SubSections}" 
                      ItemTemplate="{DynamicResource BinderTemplate}"/>
        </Expander>
    </StackPanel>
</HierarchicalDataTemplate>

EDIT: Not to steal @BDE's thunder, but he/she's mostly correct about the use of DataType - but this is the way you'd "simplify" the above XAML:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:local="clr-namespace:WpfApplication1" 
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
         <!-- normal template for sections -->
         <DataTemplate DataType="{x:Type local:SectionViewModel}">
            <GroupBox Header="{Binding Section.SectionName}">
                <ItemsControl ItemsSource="{Binding SubSections}"/>
            </GroupBox>
         </DataTemplate>
         <!-- hierarchical template for subsections -->
         <HierarchicalDataTemplate 
              DataType="{x:Type local:SubSectionViewModel}">
           <StackPanel>
               <Expander Header="{Binding SubSection.SubSectionName}">
                   <ItemsControl Margin="5,0,0,0" 
                         ItemsSource="{Binding SubSections}"/>
               </Expander>
           </StackPanel>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <Grid>
        <!-- 
           no need to specify ItemTemplate if WPF can suss out all the item types 
         -->
        <ItemsControl Name="lstMain" ItemsSource="{Binding Sections}"/>
    </Grid>
</Window>
JerKimball
  • 16,584
  • 3
  • 43
  • 55
  • Brilliant. Thanks :) I had tried doing this but tried to use the template as a static resource instead of a dynamic one and got a compile time error :) – user589195 Jan 04 '13 at 09:24
1

An alternative to HierarchicalDataTemplate is to add an ItemsControl to the Expander in your data template, and bind the ItemsSource there.

Also, since you specify the DataType in the data template definition, so you don't have to directly set the ItemTemplate by key name for your toplevel ItemsControl.

If you modifiy you XAML like this it might do what you want:

<DataTemplate DataType="{x:Type local:SubSectionViewModel}">
    <StackPanel>
        <Expander Header="{Binding SubSection.SubSectionName}">
            <ItemsControl ItemsSource="{Binding SubSections}"/>
        </Expander>
    </StackPanel>
</DataTemplate>

<ItemsControl Name="lstMain" ItemsSource="{Binding Sections}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <GroupBox Header="{Binding Section.SectionName}">
                <ItemsControl ItemsSource="{Binding SubSections}"/>
            </GroupBox>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
WildCrustacean
  • 5,896
  • 2
  • 31
  • 42
  • You need the data type on the first DataTemplate as well for the template->type inference to work (see my edit) – JerKimball Jan 03 '13 at 17:03
  • Agreed, if you want all the data templates together in the resources section. In the code that I posted though, since the template for `SectionViewModel` is directly specified in the ItemsControl and not in the resources, it should still work, correct? It looks like it works in my testing at least. – WildCrustacean Jan 03 '13 at 17:10
  • Huh. Fun..it didn't infer the template type when I threw a quick skeleton together in Blend... – JerKimball Jan 03 '13 at 17:13
  • Weird. Which versions are you using? I was testing this with VS2010, not using Blend. – WildCrustacean Jan 03 '13 at 17:19
  • Blend 4 - although i suppose it's entirely possible we vary slightly in xaml - I didn't save my little harness, so what I posted in my answer is all that remains. :( – JerKimball Jan 03 '13 at 17:26
  • Oh well, I think the point is that there are a couple of ways to implement something like this. – WildCrustacean Jan 03 '13 at 17:27
0

Section.SectionName shall be MaterialName?

Look Lines :

    <ItemsControl ItemsSource="{Binding SubSections}" ItemTemplate="{StaticResource BinderTemplate}" />

and

    <HierarchicalDataTemplate x:Key="BinderTemplate" ItemsSource="{Binding Path=SubSections}" DataType="{x:Type local:SubSectionViewModel}">

I think, if firs line binding is SubSections, so second line instead of

   {Binding Path=SubSections} 

you probably shall write

    {Binding}

SubSectionName i do not find in your classes.

Please, add more classes

zzfima
  • 1,528
  • 1
  • 14
  • 21