Thierry
Thierry

Reputation: 6458

XAML definition to build a TreeView with MVVM

I'm trying to build a TreeView using MVVM in a WPF App but I don't understand how to handle HierarchicalDataTemplate. My TreeView should represent a folder structure which contains folders within folders and so on.

My folder ViewModel is defined as follows:

public class TreeViewFolderViewModel : ViewModelBase
{
    private int _id;
    private int _parentId;
    private string _text;
    private string _key;
    private ObservableCollection<TreeViewFolderViewModel> _children;

    public int Id
    {
        get { return this._id; }
        set { Set(() => Id, ref this._id, value); }
    }

    public int ParentId
    {
        get { return this._parentId; }
        set { Set(() => ParentId, ref this._parentId, value); }
    }

    public string Text
    {
        get { return this._text; }
        set { Set(() => Text, ref this._text, value); }
    }

    public string Key
    {
        get { return this._key; }
        set { Set(() => Key, ref this._key, value); }
    }

    public ObservableCollection<TreeViewFolderViewModel> Children
    {
        get { return this._children ?? (this._children = 
              new ObservableCollection<TreeViewFolderViewModel>()); }
        set { Set(() => Children, ref this._children, value); }
    }
}

My model has the same structure as my ViewModel so the final ViewModel is a list of folders that contain child folders and so forth. I'm using recursion to load all these folders and that part is working fine.

Where I'm stuck is on how to define and load this ViewModel into the actual TreeView.

I've read Hierarchical DataBinding in TreeView using MVVM pattern and while I more or less understand what's going on, but each of the levels of the TreeView represent a different object type while my TreeView has only one object type and I'm confused as to how I'm suppose to define this.

The Root ViewModel property in my MainWindowViewModel is of type TreeViewFolderViewModel which means I have a single object which represents to root of my TreeView. This object has Children of type TreeViewFolderViewModel which in turn have also Children of type TreeViewFolderViewModel and so forth

How do I defined this in XAML? I have the following defined:

<TreeView Grid.Row="1" Margin="5,0,5,5" ItemsSource="{Binding RootFolder}"/>

And I've got a Hierarchical template defined as follows:

<Window.Resources>
    <HierarchicalDataTemplate ItemsSource="{Binding Children}" 
     DataType="{x:Type viewmodels:SharePointFolderTreeViewViewModel}">
        <Label Content="{Binding Name}"/>
    </HierarchicalDataTemplate>
</Window.Resources>

But nothing is loading up.

Any ideas on how I can resolve this?

Thanks.

Upvotes: 0

Views: 67

Answers (1)

Funk
Funk

Reputation: 11211

I prepared a small sample to illustrate.

ViewModels

public class TreeViewFolderViewModel : ViewModelBase
{
    private int id;
    public int Id
    {
        get { return id; }
        set { id = value; OnPropertyChanged("Id"); }
    }

    private string text;
    public string Text
    {
        get { return text; }
        set { text = value; OnPropertyChanged("Text"); }
    }

    private ObservableCollection<TreeViewFolderViewModel> children;
    public ObservableCollection<TreeViewFolderViewModel> Children
    {
        get
        {
            return children ?? (children =
            new ObservableCollection<TreeViewFolderViewModel>());
        }
        set { children = value; OnPropertyChanged("Children"); }
    }
}

public class TreeViewModel : ViewModelBase
{
    private List<TreeViewFolderViewModel> items;
    public List<TreeViewFolderViewModel> Items
    {
        get { return items; }
        set { items = value; OnPropertyChanged("Items"); }
    }

    public TreeViewModel()
    {
        Items = new List<TreeViewFolderViewModel>()
        {
            new TreeViewFolderViewModel()
            {
                Id =0, Text="RootFolder", Children=new ObservableCollection<TreeViewFolderViewModel>()
                {
                    new TreeViewFolderViewModel() { Id = 10, Text = "FirstFolder", Children=new ObservableCollection<TreeViewFolderViewModel>() { new TreeViewFolderViewModel() { Id = 11, Text = "FirstChild" } } } ,
                    new TreeViewFolderViewModel() { Id = 20, Text = "SecondFolder", Children = new ObservableCollection<TreeViewFolderViewModel>() { new TreeViewFolderViewModel() { Id = 21, Text = "SecondChild" } } } ,
                    new TreeViewFolderViewModel() { Id = 30, Text = "ThirdFolder", Children = new ObservableCollection<TreeViewFolderViewModel>() { new TreeViewFolderViewModel() { Id = 31, Text = "ThirdChild" } } }
                }
            }
        };
    }
}

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}

MainWindow.xaml

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:TreeViewModel />
    </Window.DataContext>

    <Window.Resources>    
        <HierarchicalDataTemplate ItemsSource="{Binding Children}" 
                                  DataType="{x:Type local:TreeViewFolderViewModel}">
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding StringFormat="{}{0} {1}">
                        <Binding Path="Id" />
                        <Binding Path="Text" />
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </HierarchicalDataTemplate>    
    </Window.Resources>

    <Grid>
        <TreeView ItemsSource="{Binding Items}" />
    </Grid>
</Window>

Upvotes: 1

Related Questions