Alexey Titov
Alexey Titov

Reputation: 97

Need help making a 'nested' WPF ItemsControl

Can anybody please tell what im missing trying to make a 'nested' WPF ItemsControl ... if it even can work the way I want it. I have this data model:

public class PlateItem
{
    public Guid ID { get; set; }
    public string Type { get; set; }
    public string Caption { get; set; }
    public int GridRow { get; set; }
    public int GridColumn { get; set; }

    // 0 or 2 Children
    public ObservableCollection<PlateItem> Children { get; set; }
}

private void Initialize()
{
    ObservableCollection<PlateItem> collection = new ObservableCollection<PlateItem>();

    // Type = "Child" - Caption should be displayed
    // Type = "Container" - Caption should not be displayed. Its Children of type "Visual" Captions should be displayed

    PlateItem item_Main = new PlateItem() { Type = "Container", Caption = "ZERO", GridRow = 0, GridColumn = 0, ID = Guid.NewGuid(), Children = new ObservableCollection<PlateItem>() };
    PlateItem item_Main_A = new PlateItem() { Type = "Child", Caption = "ONE", GridRow = 0, GridColumn = 0, ID = Guid.NewGuid(), Children = new ObservableCollection<PlateItem>() };
    PlateItem item_Main_B = new PlateItem() { Type = "Container", Caption = "TWO", GridRow = 1, GridColumn = 0, ID = Guid.NewGuid(), Children = new ObservableCollection<PlateItem>() };
    PlateItem item_Main_B_1 = new PlateItem() { Type = "Child", Caption = "THREE", GridRow = 0, GridColumn = 0, ID = Guid.NewGuid(), Children = new ObservableCollection<PlateItem>() };
    PlateItem item_Main_B_2 = new PlateItem() { Type = "Child", Caption = "FOUR", GridRow = 1, GridColumn = 0, ID = Guid.NewGuid(), Children = new ObservableCollection<PlateItem>() };

    item_Main_B.Children.Add(item_Main_B_1);
    item_Main_B.Children.Add(item_Main_B_2);

    item_Main.Children.Add(item_Main_A);
    item_Main.Children.Add(item_Main_B);

    collection.Add(item_Main);

    this.PlateItemsSource = collection;
}

And I need a nested control that would:

  1. if PlateItem doesn't have Children - display a textbox with PlateItem.Caption
  2. if PlateItem has Children (max 2) - display a Grid with two Children using rule 1.

Now I have this XAML, which doesnt work:

<UserControl.Resources>

    <!-- ************************************************** -->
    <h:TemplateselectorPlateItem x:Key="TemplateselectorPlateItem"/>

    <!-- should display PlateItems that don't have Children -->
    <DataTemplate x:Key="DatatemplatePlateItemSingle">
        <TextBox Background="Green" Margin="20"
                 Text="{Binding Path=Caption, Mode=TwoWay}" />
    </DataTemplate>

    <!-- should display PlateItems that has Children        -->
    <DataTemplate x:Key="DatatemplatePlateItemDouble">
        <ItemsControl ItemsSource="{Binding Path=Children}"
                      ItemTemplate="{StaticResource DatatemplatePlateItemSingle}">

            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Style.Setters>
                        <Setter Property="Grid.Row" Value="{Binding Path=GridRow}"/>
                        <Setter Property="Grid.Column" Value="{Binding Path=GridColumn}"/>
                    </Style.Setters>
                </Style>
            </ItemsControl.ItemContainerStyle>

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DataTemplate>

</UserControl.Resources>

<ItemsControl ItemsSource="{Binding Path=PlateItemsSource}"
              ItemTemplateSelector="{StaticResource TemplateselectorPlateItem}">
</ItemsControl>

Code for the TemplateSelector:

public class TemplateselectorPlateItem : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement element = container as FrameworkElement;

        if (element != null && item != null && item is PlateItem)
        {
            PlateItem plateItem = item as PlateItem;

            if(plateItem.Children.Count == 0)
            {
                return element.FindResource("DatatemplatePlateItemSingle") as DataTemplate;
            }
            else
            {
                return element.FindResource("DatatemplatePlateItemDouble") as DataTemplate;
            }
        }

        return null;
    }
}

Very rough visual representation of what Im trying to achieve http://www.trinitytilesupply.com/pattern2.jpg Why I want to use such model - I will need functionality to split any "Visual" vertically or horisontally, in code that would mean - change a source PlateItem: "Child"-type to "Container"-type with 2 new children

Upvotes: 0

Views: 438

Answers (1)

Tedy Pranolo
Tedy Pranolo

Reputation: 1425

You need to tweak DatatemplatePlateItemDouble, remove its ItemTemplate, instead use the same ItemTemplateSelector. So it would recursively apply DatatemplatePlateItemDouble to all descendants with children.

   <DataTemplate x:Key="DatatemplatePlateItemDouble">
        <ItemsControl ItemsSource="{Binding Path=Children}"
                      ItemTemplateSelector="{StaticResource TemplateselectorPlateItem}" >
  ...

You also need to change/remove this line which will cause infinite recursion.

  item_Main.Children.Add(item_Main);

Once you do that you should get The produced image

Upvotes: 1

Related Questions