在树视图 MVVM 中获取父项

Get parent item in treeview MVVM

我尝试学习使用 treeview base one MVVM。我参考这个教程 https://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode

它很有帮助,我尝试通过重写代码来理解它

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>

我的源代码工作正常,我可以从选定节点添加子节点、删除第一级节点、重命名选定节点、获取选定节点。但只有一件事,我不知道如何存储和获取父节点

我仔细阅读了 Josh 教程,它似乎是父节点存储在构造函数中。下面是 Josh 的代码。如果我这样使用,父节点是 TreeViewViewModel,而不是节点。我不想那样,有什么办法可以使父节点成为 TreeNode 类型。我想像这样使用 _treeView.Seletect.Parent.Name 它应该打印所选节点的父名称。

    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>());
    }

父项不应该是 TreeViewViewModel,应该是 TreeNode

你的 TreeNode 相当于乔什的 PersonViewModel

在乔希的世界里:

Person是模型(数据)

PersonViewModel 表示该数据被呈现为树项。

PersonViewModel 构造函数有两个参数。要显示的数据 (Person),以及对父树项的引用 (PersonViewModel)。然后,构造函数通过迭代数据的子项来创建所有子树项 (PersonViewModel)。 (Person)

在你的世界里:

??是型号(没有)

TreeNode 是一些数据的表示,其名称和 ID 被呈现为树项。换句话说 TreeNode 既是模型又是视图模型(这不一定是错误的)

但是,您试图在视图中构建数据,这是错误的。您还在构建树 向后,从叶子到根。由于您首先构建子项,因此无法设置父项,因为此时父项不存在。

在 Josh 的世界中,层次结构已经存在于模型中,因此父级可用。如果您同时构建 model/VM,则需要以不同的方式构建您的树,以便在创建子节点之前先存在父节点。