1
votes

I try to learn using treeview base one MVVM. I refer this tutorial https://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode

It's very helpful and I try to understand it by rewrite code

TreeViewViewModel.cs

public class TreeViewViewModel
{
    private ObservableCollection<TreeNode> _firstNode;
    public static TreeNode _seletectedNode;
    private ObservableCollection<TreeNode> _node;

    private ICommand _addCommand;

    public TreeViewViewModel(TreeNode rootNode) : this(rootNode, null) {}

    public TreeViewViewModel(TreeNode rootNode, TreeViewViewModel parentNode)
    {
        _firstNode = new ObservableCollection<TreeNode>(rootNode.Node);
        _node = new ObservableCollection<TreeNode>((from child in rootNode.Node select child).ToList<TreeNode>());

        _addCommand = new AddCommand(this);
    }

    public ObservableCollection<TreeNode> FirstNode
    {
        get { return _firstNode; }
        set { _firstNode = value; }
    }

    public ObservableCollection<TreeNode> Node
    {
        get { return _node; }
        set { _node = value; }
    }

    public TreeNode Selected
    {
        get{ return _seletectedNode; }
        set{ _seletectedNode = value;}
    }

    public ICommand AddCommand
    {
        get { return _addCommand; }
    }
}

TreeNode.cs

public class TreeNode : INotifyPropertyChanged
{
    private ObservableCollection<TreeNode> _Node = new ObservableCollection<TreeNode>();

    private string _Name;
    private string _ID;

    private bool _isExpanded;
    private bool _isSelected;

    public ObservableCollection<TreeNode> Node
    {
        get { return _Node; }
        set
        {
            _Node = value;
            OnPropertyChanged("Node");
        }
    }

    public string Name
    {
        get { return _Name; }
        set { _Name = value; OnPropertyChanged("Name"); }
    }

    public string ID
    {
        get { return _ID; }
        set { _ID = value; OnPropertyChanged("ID");}
    }

    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (value != _isExpanded)
            {
                _isExpanded = value;
                this.OnPropertyChanged("IsExpanded");
            }
        }
    }   

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                this.OnPropertyChanged("IsSelected");

                if (_isSelected) {TreeViewViewModel._seletectedNode = this;}
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

}

AddCommand.cs

public class AddCommand : ICommand
{
    private TreeViewViewModel _TreeView;

    public AddCommand(TreeViewViewModel treeView)
    {
        _TreeView = treeView;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        //Show Selected item
        //MessageBox.Show(_TreeView.Selected.Name);

        //Add first level item
        //_TreeView.FirstNode.Add(new TreeNode { Name = "Hihi" });

        //Rename selected item
        //_TreeView.Selected.Name = "Hello";

        //Remove first level item
        //_TreeView.FirstNode.Remove(_TreeView.Selected);

        //Add selected item
        //_TreeView.Selected.Node.Add(new TreeNode { Name = "Hihi" });

    }
}

MainWindown.xaml.cs

public partial class MainWindow : Window
{
    public TreeViewViewModel _TreeView;

    public MainWindow()
    {
        InitializeComponent();

        TreeNode rootNode = new TreeNode
        {
            Name = "David",
            Node =
            {
                new TreeNode
                {
                    Name = "Alberto",
                    Node =
                    {
                        new TreeNode
                        {
                            Name = "Zena",
                            Node =
                            {
                                new TreeNode
                                {
                                    Name = "Nick",
                                }
                            }
                        },
                        new TreeNode
                        {
                            Name = "Sarah",
                        },
                    }
                },
                new TreeNode
                {
                    Name = "Komrade",
                }
            }
        };


        _TreeView = new TreeViewViewModel(rootNode);

        base.DataContext = _TreeView;
    }
}

MainWindow.xaml

<Window x:Class="TreeviewMVVM_Test.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:local="clr-namespace:TreeviewMVVM_Test"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>

        <TextBox Margin="10,35,380,260"/>

        <Button Margin="154,27,324,268"  Command="{Binding AddCommand}" />



        <TreeView x:Name="treeView"
                            ItemsSource="{Binding FirstNode}" 
                            BorderThickness="0"
                            Height="Auto" Width="Auto"
                            Margin="21,84,349,19">

            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                    <Setter Property="FontWeight" Value="Normal" />
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="FontWeight" Value="Bold" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>

            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Node}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

My source code is working fine, I can add childnode from selected node, remove first level node, rename selected node, get selected node. But only 1 thing, I don't know how to store and get parent's node

I read thought out the Josh tutorial, it's seem to be parent node is stored in contructor. Here below is Josh's code. If i use like that Parent node is a TreeViewViewModel, not a node. I don't want like that, Is there any way to make parent node to TreeNode type. I want to use like this _treeView.Seletect.Parent.Name and it's should print parent name of selected node.

    private PersonViewModel(Person person, PersonViewModel parent)
    {
        _person = person;
        _parent = parent;
        _children = new ObservableCollection<PersonViewModel>(
                (from child in _person.Children
                 select new PersonViewModel(child, this))
                 .ToList<PersonViewModel>());
    }
1
I don't want like that care to explain why don't you like it? in MVVM if you feel the need to access parent item, then you know you're doing something wrong. You can access the parent VM by adding a parent property, there shouldn't be any reason to access the parent View. you can implement something that finds the parent view and then read its data context but that breaks MVVM.Bizhan
If Josh's personviewmodel doesn't have a Parent property then just add one which returns the parent handed in and stored in _parent. Build your family of viewmodels an instance at a time so you build a parent and have an object you can then pass into the ctor of the children. You can then use the parent property of any given personviewmodel instance, either within it or externally.Andy
Hi Bizhan Hi @Andy I understood my issue. I was wrong to apply MVVM like that. I will try to build a correct MVVM again.Bruce
I have update code following Josh's tutorial, but my problem I now can't access to PersonViewModel, It was prevent by midde class-FamilyTreeViewModel. I create a new post and update with new source to here stackoverflow.com/questions/62342745/… please help me take a look to itBruce

1 Answers

2
votes

The parent should not be a TreeViewViewModel, it should be a TreeNode.

Your TreeNode is the equivalent of Josh's PersonViewModel.

In Josh's world:

Person is the model (data)

PersonViewModel is a representation of that data being presented as a tree item.

The PersonViewModel constructor takes two arguments. The data to display (Person), and a reference to the parent tree item (PersonViewModel). The constructor then creates all child tree items (PersonViewModel) by iterating the child items of the data. (Person)

In your world:

?? is the model (there isn't any)

TreeNode is a representation of some data with a name and ID being presented as a tree item. In other words TreeNode is both the model and viewmodel (which isn't necessarily wrong)

However, you are attempting to construct your data in the view, which is wrong. You are also constructing your tree backwards, from the leaves to the root. Since you are constructing children first, you cannot set the parent because the parent doesn't exist at that point.

In Josh's world the hierarchy already exists in the model, so the parent is available. If you are constructing model/VM at the same time, you will need to build your tree a different way so that the parent exists before the children are created.