Are recursive DataTemplates possible?
Asked Answered
M

2

17

I have a class something like:

public class Section
{
    private IEnumerable<Section> sections;
    private IEnumerable<KeyValuePair<string, string>> attributes

    public string Name {get;set;}
    public IEnumerable<Section> Sections
    {
        get {return sections;}
    }
    public IEnumerable<KeyValuePair<string, string>> Attributes
    {
        get {return attributes;}
    }

    public Section(...)
    {
        //constructor logic
    }
}

Which is contained in another class, lets call it OtherClass for sake of argument, that wraps around it and is used in WPF in an ObjectDataProvider.

As you can see, an instance of Section can contain many other instances of Section.

Is there a way in XAML to create a template that deals with this recursion?

This is what I've got so far:

<UserControl x:Class="OtherClassEditor"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:OtherClassNS="clr-namespace:NameSpace"
    Height="300" Width="300">
    <UserControl.Resources>
        <ObjectDataProvider ObjectType="{x:Type OtherClassNS:OtherClass}" x:Key="ViewModel" />
        <DataTemplate x:Key="listViewAttr">
            <WrapPanel>
                <TextBlock Text="{Binding Path=Key}"></TextBlock><TextBlock Margin="0,0,4,0">: </TextBlock>
                <TextBlock Text="{Binding Path=Value}"></TextBlock>
            </WrapPanel>
        </DataTemplate>
        <DataTemplate x:Key="listViewSection">
            <StackPanel>
                <WrapPanel Margin="0,0,0,8">
                    <TextBlock Margin="0,0,4,0">Section:</TextBlock>
                    <TextBlock Text="{Binding Path=Name}"/>
                </WrapPanel>
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="listViewData">
            <StackPanel>
                <WrapPanel Margin="0,0,0,8">
                    <TextBlock Margin="0,0,4,0">Section: </TextBlock>
                    <TextBlock Text="{Binding Path=Name}"/>
                    <ListView DataContext="{Binding Path=Sections}" ItemTemplate="{StaticResource listViewSection}" ItemsSource="{Binding Sections}">
                    </ListView>
                    <ListView DataContext="{Binding Path=Attributes}" ItemsSource="{Binding Attributes}" ItemTemplate="{StaticResource listViewAttr}">

                    </ListView>
                </WrapPanel>
            </StackPanel>    
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <ListView DataContext="{StaticResource ViewModel}" ItemTemplate="{StaticResource listViewData}" ItemsSource="{Binding Sections}">
        </ListView>
    </Grid>
</UserControl>

But I can't get recursion, as a DataTemplate can only reference one that is declared before it.

Can this be done in XAML? If so, how? Is this something that I'll have to do in code behind?

Are DataTemplates even the way to go? Is there a better way to display and edit this data?

Markland answered 21/7, 2010 at 13:47 Comment(2)
Does this work if you use DynamicResource instead of StaticResource? It would be interesting if it works, but I'd hate to see what happens if the template causes an infinite recursion.Masson
I hadn't thought of that! It does work, but I like the look of HierarchicalDataTemplate. It seems more fit for purpose.Markland
H
2

Perhaps I'm misunderstanding your scenario, but is HierarchicalDataTemplate what you're looking for?

Hebdomad answered 21/7, 2010 at 14:4 Comment(1)
HierarchicalDataTemplate only works with TreeView and MenuItem. I don't know why this is the accepted answer.Heliostat
M
31

In case anyone needs to see how to do this without using an HierarchicalDataTemplate:

<UserControl x:Class="OtherClassEditor"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:OtherClassNS="clr-namespace:NameSpace"
    Height="300" Width="300">
    <UserControl.Resources>
        <ObjectDataProvider ObjectType="{x:Type OtherClassNS:OtherClass}" x:Key="ViewModel" />
        <DataTemplate x:Key="listViewAttr">
            <WrapPanel>
                <TextBlock Text="{Binding Path=Key}"></TextBlock>
                <TextBlock Margin="0,0,4,0">: </TextBlock>
                <TextBlock Text="{Binding Path=Value}"></TextBlock>
            </WrapPanel>
        </DataTemplate>
        <DataTemplate x:Key="listViewData">
            <StackPanel>
                <WrapPanel Margin="0,0,0,8">
                    <TextBlock Margin="0,0,4,0">Section: </TextBlock>
                    <TextBlock Text="{Binding Path=Name}"/>
                </WrapPanel>
                <ListView ItemTemplate="{DynamicResource listViewData}" ItemsSource="{Binding Sections}" />
                <ListView ItemsSource="{Binding Attributes}" ItemTemplate="{StaticResource listViewAttr}" />
            </StackPanel>    
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <ListView DataContext="{StaticResource ViewModel}" ItemTemplate="{DynamicResource listViewData}" ItemsSource="{Binding Sections}">
        </ListView>
    </Grid>
</UserControl>

This gets me basically what I want.

The main change is as Dan Bryant suggested: changing the ItemTemplate to use a Dynamic rather than static resource for the recursive object (Section).

Markland answered 21/7, 2010 at 15:16 Comment(0)
H
2

Perhaps I'm misunderstanding your scenario, but is HierarchicalDataTemplate what you're looking for?

Hebdomad answered 21/7, 2010 at 14:4 Comment(1)
HierarchicalDataTemplate only works with TreeView and MenuItem. I don't know why this is the accepted answer.Heliostat

© 2022 - 2024 — McMap. All rights reserved.