Adding Custom Class as TreeViewItem to TreeView WPF

瘦欲@ 提交于 2019-12-11 07:31:43

问题


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:


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:



来源:https://stackoverflow.com/questions/54982705/adding-custom-class-as-treeviewitem-to-treeview-wpf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!