1
votes

I have scenario where my TreeView look like below:

  • MainParent
  • .>-Parent
  • ...>--Child1
  • ...>--Child2
  • ...>--Child3
  • ...>--Child4
  • ...>--Child5
  • ......> Child1_Of_Child5
  • ...........>Child1(Child1_Of_Child5)
  • ...........>Child2(Child1_Of_Child5)
  • ......> Child2_of_Child5
  • ...........>Child1(Child2_Of_Child5)
  • ...........>Child2(Child2_Of_Child5)

I am able to bind ObservableCollection till Child1/Child2/../Child5 using HierarchicalDataTemplate. Though I am able to add Child1_Of_Child5 to Child5 ObservableCollection (seen in debug mode), Same is not reflecting in UI/Treeview.

Below is my XAML code:

<TreeView.ItemTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type VM:ProjectViewModel}" ItemsSource="{Binding SelectActivityViewModelCollection}">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
                            <TextBlock Text="{Binding Header}"></TextBlock>
                        </StackPanel>
                        <HierarchicalDataTemplate.ItemTemplate>
                            <HierarchicalDataTemplate DataType="{x:Type VM:SelectActivityViewModel}" ItemsSource="{Binding ToolsViewModelCollection}"
                                     >
                                <StackPanel Orientation="Horizontal">
                                    <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
                                    <TextBlock Text="{Binding Header}"></TextBlock>
                                </StackPanel>
                                <HierarchicalDataTemplate.ItemTemplate>
                                    <HierarchicalDataTemplate DataType="{x:Type VM:ToolsViewModel}" ItemsSource="{Binding SheetsViewModelBaseCollection}">
                                        <StackPanel Orientation="Horizontal">
                                            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
                                            <TextBlock Text="{Binding Header}"></TextBlock>
                                        </StackPanel>
                                        <HierarchicalDataTemplate.ItemTemplate>
                                            <HierarchicalDataTemplate x:Name="LastLevel" DataType="{x:Type VM:ViewModelBase}" ItemsSource="{Binding FeaturesViewModelCollection}">
                                                <StackPanel Orientation="Horizontal">
                                                    <Image Source="./resources/file.png" Width="15" Height="15"/>
                                                    <TextBlock Text="{Binding Header}"/>
                                                </StackPanel>
                                            </HierarchicalDataTemplate>
                                        </HierarchicalDataTemplate.ItemTemplate>

                                    </HierarchicalDataTemplate>
                                </HierarchicalDataTemplate.ItemTemplate>
                            </HierarchicalDataTemplate>
                        </HierarchicalDataTemplate.ItemTemplate>
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>

Class Structure: (ViewModelBase is extended to all classes)

  • ProjectViewModel - has SelectActivityViewModelCollection = (ObservableCollection(SelectActivityViewModel))
  • SelectActivityViewModel - has ToolsViewModelCollection = (OC(ToolsViewModel))
  • ToolsViewModel - has SheetsViewModelBaseCollection and FeaturesViewModelCollection = (OC(ViewModelBase) and OC(FeaturesViewModel))
  • FeaturesViewModel - has UseCaseViewModelCollection = (OC(UseCaseViewModel))
  • In ToolsViewModel I am using SheetsViewModelBaseCollection to store Child1/Child2/.../Child5
  • and FeaturesViewModelCollection - to store Child1_Of_Child5/Child2_Of_Child5/../Child(n)_Of_Child5
  • EDIT : As i have extended ViewModelBase to all classes, i am using OC(ViewModelBase) to store different sheets(.i.e.,in SheetsViewModelCollection). Note:

    1. No DataBase is used: Serialisation and Deserialisation is used
    2. WPF : MVVM pattern is followed

Image shows complete TreeView and OC of respective classes

1
Thanks for the response Dymanoid! and I am sorry that i made a mistake in description. SheetsViewModelCollection is OC(ViewModelBase). As i have extended ViewModelBase to all classes, i am using OC(ViewModelBase) to store different sheets. To generally specify a class type i am using ViewModelBase in XAML.User1312
Not sure it matters anyhow but Dymanoid is mistaken. Datatemplates will work with base classes. I think he has got mixed up with styles and targettype.Andy

1 Answers

1
votes

I have tried to reproduce your problem in a sample project.

According to your description, ToolsViewModel has two collections (of SheetsViewModel and of FeaturesViewModel). If you want to show both of these in the tree, you will have to make a single collection containing all of these objects.

I have created an additional collection called ViewModelCollection to show how this might work.

For my tests, I have defined classes as follows:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TreeViewTest
{
  class ViewModelBase
  {
  }

  class ViewModel
  {
    public ObservableCollection<ProjectViewModel>  ProjectViewModelCollection { get; private set; }

    public ViewModel ( )
    {
      ProjectViewModelCollection = new ObservableCollection<ProjectViewModel>() ;
      ProjectViewModelCollection.Add ( new ProjectViewModel() ) ;
      ProjectViewModelCollection.Add ( new ProjectViewModel() ) ;
    }
  }

  class ProjectViewModel
  {
    public ObservableCollection<SelectActivityViewModel>  SelectActivityViewModelCollection { get; private set; }
    public string Header { get ; } = "ProjectViewModel" ;

    public ProjectViewModel ( )
    {
      SelectActivityViewModelCollection = new ObservableCollection<SelectActivityViewModel>() ;
      SelectActivityViewModelCollection.Add ( new SelectActivityViewModel() ) ;
      SelectActivityViewModelCollection.Add ( new SelectActivityViewModel() ) ;
    }

  }

  class SelectActivityViewModel
  {
    public ObservableCollection<ToolsViewModel>  ToolsViewModelCollection  { get; private set; }
    public string Header { get ; } = "SelectActivityViewModel" ;

    public SelectActivityViewModel ( )
    {
      ToolsViewModelCollection = new ObservableCollection<ToolsViewModel>() ;
      ToolsViewModelCollection.Add ( new ToolsViewModel() ) ;
      ToolsViewModelCollection.Add ( new ToolsViewModel() ) ;
    }
  }

  class ToolsViewModel
  {
    public ObservableCollection<SheetsViewModel>    SheetsViewModelCollection    { get; private set; }
    public ObservableCollection<FeaturesViewModel>  FeaturesViewModelCollection  { get; private set; }
    public ObservableCollection<ViewModelBase>      ViewModelCollection          { get; private set; }
    public string Header { get ; } = "ToolsViewModel" ;

    public ToolsViewModel ( )
    {
      SheetsViewModelCollection = new ObservableCollection<SheetsViewModel>() ;
      SheetsViewModelCollection.Add ( new SheetsViewModel() ) ;
      SheetsViewModelCollection.Add ( new SheetsViewModel() ) ;

      FeaturesViewModelCollection = new ObservableCollection<FeaturesViewModel>() ;
      FeaturesViewModelCollection.Add ( new FeaturesViewModel() ) ;
      FeaturesViewModelCollection.Add ( new FeaturesViewModel() ) ;

      // Make a single collection with all children
      ViewModelCollection = new ObservableCollection<ViewModelBase>() ;
      foreach ( var o in SheetsViewModelCollection )
        ViewModelCollection.Add ( o ) ;
      foreach ( var o in FeaturesViewModelCollection )
        ViewModelCollection.Add ( o ) ;
    }
  }

  class SheetsViewModel : ViewModelBase
  {
    public string Header { get ; } = "SheetsViewModel" ;
  }

  class FeaturesViewModel : ViewModelBase
  {
    public ObservableCollection<UseCaseViewModel>  UseCaseViewModelCollection   { get; private set; }
    public string Header { get ; } = "FeaturesViewModel" ;

    public FeaturesViewModel ( )
    {
      UseCaseViewModelCollection = new ObservableCollection<UseCaseViewModel>() ;
      UseCaseViewModelCollection.Add ( new UseCaseViewModel() ) ;
      UseCaseViewModelCollection.Add ( new UseCaseViewModel() ) ;
    }
  }

  class UseCaseViewModel
  {
    public string Header { get ; } = "UseCaseViewModel" ;
  }
}

I have made some changes to the XAML:

(1) I have defined additional templates for:

  • FeaturesViewMode
  • SheetsViewModel
  • UseCaseViewModel

(2) I have defined the HierarchicalDataTemplate as resources without any kind of nesting.

This is personal preference. I think that your approach with HierarchicalDataTemplate.ItemTemplate also works.

<Window x:Class="TreeViewTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:VM="clr-namespace:TreeViewTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
  <Grid>

    <TreeView ItemsSource="{Binding ProjectViewModelCollection}">

      <TreeView.Resources>

        <HierarchicalDataTemplate DataType="{x:Type VM:ProjectViewModel}" ItemsSource="{Binding SelectActivityViewModelCollection}">
          <StackPanel Orientation="Horizontal">
            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
            <TextBlock Text="{Binding Header}"></TextBlock>
          </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type VM:SelectActivityViewModel}" ItemsSource="{Binding ToolsViewModelCollection}">
          <StackPanel Orientation="Horizontal">
            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
            <TextBlock Text="{Binding Header}"></TextBlock>
          </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type VM:ToolsViewModel}" ItemsSource="{Binding ViewModelCollection}">
          <StackPanel Orientation="Horizontal">
            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
            <TextBlock Text="{Binding Header}"></TextBlock>
          </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type VM:FeaturesViewModel }" ItemsSource="{Binding UseCaseViewModelCollection}">
          <StackPanel Orientation="Horizontal">
            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
            <TextBlock Text="{Binding Header}"></TextBlock>
          </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type VM:SheetsViewModel}">
          <StackPanel Orientation="Horizontal">
            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
            <TextBlock Text="{Binding Header}"></TextBlock>
          </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type VM:UseCaseViewModel}">
          <StackPanel Orientation="Horizontal">
            <Image Source="./resources/New_Package.png" Width="15" Height="15"/>
            <TextBlock Text="{Binding Header}"></TextBlock>
          </StackPanel>
        </HierarchicalDataTemplate>

      </TreeView.Resources>
    </TreeView>

  </Grid>
</Window>

The DataContext is initialised in code behind as follows. There are other ways to do this.

public MainWindow ( )
{
  DataContext = new ViewModel() ;
  InitializeComponent ();
}

I was able to show all six types on five levels as shown below:

Screen shot