1

I have a class, which looks like

public class MyClass 
{
    public string MyTitle{get;set;}
    public MyClass Child{get;set;
}

The reason for this is there will only ever be a 1 to 1 relationship between child and parent (meaning, no parent will have multiple children).

I want to lay this out in a WPF application, where each MyTitle will be displayed horizontally (based upon how many parent and children there are in total). EG, something like

MyClass.MyTitle       MyClass.Child.MyTitle       MyClass.Child.Child.MyTitle  etc

In the web world we can add loops and if statements, simply checking if the child is null (or not) and then appending the next item.

In the web world, something like (psuedo code)

item = Model.MyClass;

do while(true) {    
    if (item != null) {
        <div class="floatLeft">item.MyTitle</div>
    }
    else {
        break;
    }
    item = parent.Child;
}

XAML appears to limit me to Templates. Whilst I'm sure I'd work out a C# solution and then bind directly, I'm using this as an opportunity to learn more about XAML.

The problem is, it isn't a list so I'm not sure if using ItemsControl/ListView/etc is the correct approach. I'm not even sure if what I want can be done.

So my question is simply can this be done using XAML only?

MyDaftQuestions
  • 4,487
  • 17
  • 63
  • 120

2 Answers2

3

A simple DataTemplate like shown below would do the job. By setting its DataType property, it will automatically be applied to the Content of ContentPresenters, ContentControls, etc. when the type of the Content matches DataType, and this will also work recursively.

<Window.Resources>
    <DataTemplate DataType="{x:Type local:MyClass}">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="4" Text="{Binding MyTitle}"/>
            <ContentPresenter Content="{Binding Child}"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>

Given that the DataContext of your window contains a MyClass instance, you could just write

<ContentControl Content="{Binding}"/>

and the DataTemplate would automatically be applied recursively.

You may try with setting the DataContext like

DataContext = new MyClass
{
    MyTitle = "Item1",
    Child = new MyClass
    {
        MyTitle = "Item2",
        Child = new MyClass
        {
            MyTitle = "Item3",
        }
    }
};
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Where would you sugest the DataTemplate goes? – MyDaftQuestions Apr 22 '16 at 08:40
  • In the answer, the DataTemplate is in the Window's Resources. The ContentControl is somewhere in the window's XAML, and assumes that the DataContext of the Window contains a MyClass instance. – Clemens Apr 22 '16 at 08:41
  • Oh I'm sorry. I understand now. It doesn't make sense to me yet from reading your code (in my mind it *has* to use a repeater of some sorts where it clearly doesn't) so please give me a few minutes to test and play? :) – MyDaftQuestions Apr 22 '16 at 08:42
  • A minor edit: I replaced ContentControl in the DataTemplate by ContentPresenter. See here: http://stackoverflow.com/a/1288353/1136211 – Clemens Apr 22 '16 at 08:50
  • This is great, but is it possible to use a StaticResource and a key to name the DataTemplate and reference it from the ContentControl? Also, why did you change to ContentPresenter as it worked with ContentControl? – MyDaftQuestions Apr 22 '16 at 09:00
  • Using a key wouldn't make sense, because you wouldn't be able to set the ContentTemplate of the ContentPresenter inside the DataTemplate to a StaticResource with that key. For why ContentPresenter, please refer to the StackOverflow post linked in my previous comment. – Clemens Apr 22 '16 at 09:07
  • Of course it won't. Yes, this fully makes sense now! Thank you – MyDaftQuestions Apr 22 '16 at 09:16
2

if MyClass always have fixed number of child MyClass element, it is possible to make a recursive template

<Window x:Class="WpfApplication2.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpfApplication2="clr-namespace:WpfApplication2"
        Title="TestWindow" Height="300" Width="300">
    <Window.Resources>
    <DataTemplate DataType="{x:Type wpfApplication2:MyClass}">
        <WrapPanel Orientation="Horizontal">
            <TextBlock Text=" => " FontSize="18" Margin="5,0"/>
            <TextBlock Text="{Binding MyTitle}" FontSize="18"/>
            <ContentControl Content="{Binding Child}"/>
        </WrapPanel>
    </DataTemplate>    

    </Window.Resources>

    <Grid>
      <Border BorderBrush="Blue" BorderThickness="1" 
            Margin="5" VerticalAlignment="Top">
        <ContentControl Content="{Binding .}"/>
      </Border>
    </Grid>
</Window>
public partial class TestWindow : Window
{
    public TestWindow()
    {
        InitializeComponent();
        // test DataContext
        DataContext = new MyClass
        {
            MyTitle = "1234567890",
            Child = new MyClass
            {
                MyTitle = "qwerty uiop ",
                Child = new MyClass
                {
                    MyTitle = "asdf ghjkl"
                }
            }
        };
    }
}

enter image description here

ASh
  • 34,632
  • 9
  • 60
  • 82
  • you wrote `1 to 1 relationship`. It means any given `MyClass` instance has 1 child. what's wrong? the height of tree can vary but template will handle it – ASh Apr 22 '16 at 08:47
  • @MyDaftQuestions What do you mean by the child elements are not "fixed"? – default Apr 22 '16 at 09:02