Wpf 在treview MVVM中获取所选项目

Wpf 在treview MVVM中获取所选项目,wpf,mvvm,treeviewitem,Wpf,Mvvm,Treeviewitem,我试图通过参考Josh的教程将MVVM应用于treeview 这是我的全部源代码 TreeNode.cs public class TreeNode { private ObservableCollection<TreeNode> _children = new ObservableCollection<TreeNode>(); public ObservableCollection<TreeNode> Children {

我试图通过参考Josh的教程将MVVM应用于treeview

这是我的全部源代码

TreeNode.cs

public class TreeNode
{
    private ObservableCollection<TreeNode> _children = new ObservableCollection<TreeNode>();

    public ObservableCollection<TreeNode> Children
    {
        get { return _children; }
        set { _children = value; }
    }

    public string Name { get; set; }

    public string ID { get; set; }

}
我的目标是,当我单击添加命令时,它将显示选定项名称和父选定项名称。但问题是TreeView模型中没有任何东西可以访问TreeNodeView模型


中产阶级的TreeViewModel阻止他们在乔希的世界里访问TreeNodeViewModel的财产

,根据我对你前面问题的评论。将SelectedNode添加到TreeViewViewModel,并在节点选定设置器中进行设置:

private TreeNodeViewModel(TreeNode node, TreeNodeViewModel parent, TreeViewViewModel root)
{
    _node = node;
    _parent = parent;  
    _root = root;
    //snip rest
}

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

    }
}

如果您关心应用程序的性能,则始终在绑定源上实现
INotifyPropertyChanged
——即使您没有引发
PropertyChanged
事件(因为属性不会更改)

在您的例子中,
TreeNode
充当
TreeView
的绑定源。因此,
TreeNode
应该实现
INotifyPropertyChanged

由于
IsSelected
IsExpanded
肯定是节点的属性,因此它们应该是
TreeNode
的成员。这使得
TreeNodeViewModel
冗余。删除
TreeNodeViewModel
还将删除重复的属性,例如
子属性
名称
及其笨拙的初始化。
因此,在我的示例中,我仅使用
TreeNode
treeviwmodel
合并了这两个类

还要检查
ICommand.CanExecuteChanged
事件的正确实现。我已经在我的例子中修正了它

使用
nameof
,而不是将成员或类型的字符串值传递给方法。我还修复了在调用
OnPropertyChanged
时出现的问题。有关
OnPropertyChanged
事件invocator的最佳实现,请参阅

树的操作(例如,向树中添加/从树中删除项目)应始终集中在管理树的类中进行,例如,
TreeViewModel
。我在这个类中实现了相应的方法。命令现在调用此方法来操作树

问题的解决方案是将
SelectedNode
属性添加到管理树的类中,在您的示例中,该类就是
treeviwmodel
treeviwmodel
侦听
TreeNode
SelctedChanged
事件以更新
treeviwmodel.SelectedNode
属性

TreeNode.cs

public class TreeNode : INotifyPropertyChanged
{
    public event EventHandler SelectedChanged;
    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection<TreeNode> _children;
    private TreeNode _parent;
    private TreeNode _selectedTreeNode;
    private string _name;
    private string _id;
    private bool _isExpanded;
    private bool _isSelected;

    public TreeNode() => this(null);

    public TreeNode(TreeNode parent)
    {
        _parent = parent;    
        _children = new ObservableCollection<TreeNodeViewModel>();
    }

    public ObservableCollection<TreeNode> Children
    {
        get => _children; 
        set
        {
            if (value != _children)
            {
                _children = value;
                OnPropertyChanged(nameof(this.Children));
            }
        }
    }

    public TreeNode Parent
    {
        get => _parent; 
        set
        {
            if (value != _parent)
            {
                _parent = value;
                OnPropertyChanged(nameof(this.Parent));
            }
        }
    }

    public string Name
    {
        get => _name; 
        set
        {
            if (value != _name)
            {
                _name = value;
                OnPropertyChanged(nameof(this.Name));
            }
        }
    }

    public string Id
    {
        get => _id; 
        set
        {
            if (value != _id)
            {
                _id = value;
                OnPropertyChanged(nameof(this.Id));
            }
        }
    }

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

            // Expand all the way up to the root.
            if (_isExpanded && _parent != null)
                _parent.IsExpanded = true;
        }
    }

    public bool IsSelected
    {
        get => _isSelected; 
        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                OnPropertyChanged(nameof(this.IsSelected));

                OnSelectionChanged();
            }
        }
    }

    private void OnSelectionChanged()
    {
        this.SelectedChanged?.Invoke(this, EventArgs.Empty);
    }

    private void OnPropertyChanged(string propertyName)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class TreeViewModel : INotifyPropertyChanged
{
    private readonly ObservableCollection<TreeNode> _firstLevel;
    private readonly TreeNode _rootNode;    
    private TreeNode _selectedTreeNode;
    private ICommand _addCommand;
    private ICommand _removeCommand;

    public TreeViewModel(TreeNode rootNode)
    {
        _rootNode = rootNode;

        _firstLevel = new ObservableCollection<TreeNode>(_rootNode.Children);

        _addCommand = new AddCommand(this);
        _removeCommand = new RemoveCommand(this);
    }

    public void AddNode(TreeNode treeNode)
    {
        treeNode.SelectedChanged += OnTreeNodeSelectedChanged;
        this.FirstLevel.Add(treeNode);
    }

    public void RemoveNode(TreeNode treeNode)
    {
        treeNode.SelectedChanged -= OnTreeNodeSelectedChanged;
        this.FirstLevel.Remove(treeNode);
    }

    public void OnTreeNodeSelectedChanged(object sender, EventArgs e)
    {
        var treeNode = sender as TreeNode;
        if (treeNode.isSelected)
        {
            this.SelectedNode = treeNode;
        }
    }

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

    public TreeNode RootNode => _rootNode; 

    public TreeNode SelectedNode
    {
        get => _selectedNode; 
        set
        {
            if (value != _selectedNode)
            {
                _selectedNode = value;
                OnPropertyChanged(nameof(this.SelectedNode));
            }
        }
    }

    public ObservableCollection<TreeNode> FirstLevel => _firstLevel; 

    public ICommand AddCommand => _addCommand;     

    public ICommand RemoveCommand => _removeCommand; 
}
public class AddCommand : ICommand
{
    private TreeViewModel _tree;

    public AddCommand(TreeViewModel tree)
    {
        _tree = tree;
    }

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

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

    public void Execute(object parameter)
    {
        _tree.AddNode(new TreeNode(_tree.RootNode));

        //Show Selected item's name
        MessageBox.Show(_tree.SelectedNode.Name);
    }
}
public class AddCommand : ICommand
{
    private TreeViewModel _tree;

    public AddCommand(TreeViewModel tree)
    {
        _tree = tree;
    }

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

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

    public void Execute(object parameter)
    {
        _tree.RemoveNode(parameter as TreeNode);
    }
}
RemoveCommand.cs

public class TreeNode : INotifyPropertyChanged
{
    public event EventHandler SelectedChanged;
    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection<TreeNode> _children;
    private TreeNode _parent;
    private TreeNode _selectedTreeNode;
    private string _name;
    private string _id;
    private bool _isExpanded;
    private bool _isSelected;

    public TreeNode() => this(null);

    public TreeNode(TreeNode parent)
    {
        _parent = parent;    
        _children = new ObservableCollection<TreeNodeViewModel>();
    }

    public ObservableCollection<TreeNode> Children
    {
        get => _children; 
        set
        {
            if (value != _children)
            {
                _children = value;
                OnPropertyChanged(nameof(this.Children));
            }
        }
    }

    public TreeNode Parent
    {
        get => _parent; 
        set
        {
            if (value != _parent)
            {
                _parent = value;
                OnPropertyChanged(nameof(this.Parent));
            }
        }
    }

    public string Name
    {
        get => _name; 
        set
        {
            if (value != _name)
            {
                _name = value;
                OnPropertyChanged(nameof(this.Name));
            }
        }
    }

    public string Id
    {
        get => _id; 
        set
        {
            if (value != _id)
            {
                _id = value;
                OnPropertyChanged(nameof(this.Id));
            }
        }
    }

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

            // Expand all the way up to the root.
            if (_isExpanded && _parent != null)
                _parent.IsExpanded = true;
        }
    }

    public bool IsSelected
    {
        get => _isSelected; 
        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                OnPropertyChanged(nameof(this.IsSelected));

                OnSelectionChanged();
            }
        }
    }

    private void OnSelectionChanged()
    {
        this.SelectedChanged?.Invoke(this, EventArgs.Empty);
    }

    private void OnPropertyChanged(string propertyName)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class TreeViewModel : INotifyPropertyChanged
{
    private readonly ObservableCollection<TreeNode> _firstLevel;
    private readonly TreeNode _rootNode;    
    private TreeNode _selectedTreeNode;
    private ICommand _addCommand;
    private ICommand _removeCommand;

    public TreeViewModel(TreeNode rootNode)
    {
        _rootNode = rootNode;

        _firstLevel = new ObservableCollection<TreeNode>(_rootNode.Children);

        _addCommand = new AddCommand(this);
        _removeCommand = new RemoveCommand(this);
    }

    public void AddNode(TreeNode treeNode)
    {
        treeNode.SelectedChanged += OnTreeNodeSelectedChanged;
        this.FirstLevel.Add(treeNode);
    }

    public void RemoveNode(TreeNode treeNode)
    {
        treeNode.SelectedChanged -= OnTreeNodeSelectedChanged;
        this.FirstLevel.Remove(treeNode);
    }

    public void OnTreeNodeSelectedChanged(object sender, EventArgs e)
    {
        var treeNode = sender as TreeNode;
        if (treeNode.isSelected)
        {
            this.SelectedNode = treeNode;
        }
    }

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

    public TreeNode RootNode => _rootNode; 

    public TreeNode SelectedNode
    {
        get => _selectedNode; 
        set
        {
            if (value != _selectedNode)
            {
                _selectedNode = value;
                OnPropertyChanged(nameof(this.SelectedNode));
            }
        }
    }

    public ObservableCollection<TreeNode> FirstLevel => _firstLevel; 

    public ICommand AddCommand => _addCommand;     

    public ICommand RemoveCommand => _removeCommand; 
}
public class AddCommand : ICommand
{
    private TreeViewModel _tree;

    public AddCommand(TreeViewModel tree)
    {
        _tree = tree;
    }

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

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

    public void Execute(object parameter)
    {
        _tree.AddNode(new TreeNode(_tree.RootNode));

        //Show Selected item's name
        MessageBox.Show(_tree.SelectedNode.Name);
    }
}
public class AddCommand : ICommand
{
    private TreeViewModel _tree;

    public AddCommand(TreeViewModel tree)
    {
        _tree = tree;
    }

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

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

    public void Execute(object parameter)
    {
        _tree.RemoveNode(parameter as TreeNode);
    }
}

用于绑定到树视图中选定项的MVVM纯方法是使用类

公共类perTreeViewHelper:行为
{
公共对象边界SelectedItem
{
get=>GetValue(BoundSelectedItemProperty);
set=>SetValue(BoundSelectedItemProperty,value);
}
公共静态只读从属属性BoundSelectedItemProperty=
DependencyProperty.Register(“BoundSelectedItem”,
类型(对象),
类型(perTreeViewHelper),
新的FrameworkPropertyMetadata(空,
默认情况下,FrameworkPropertyMetadataOptions.BindsTwoWay,
OnBoundSelectedItemChanged);
BoundSelectedItemChanged上的私有静态无效(DependencyObject对象、DependencyPropertyChangedEventArgs参数)
{
如果(args.NewValue为perTreeViewItemViewModelBase项)
{
item.IsSelected=true;
}
}
受保护的覆盖无效附加()
{
base.onatached();
AssociatedObject.SelectedItemChanged+=OnTreeViewSelectedItemChanged;
}
附加时受保护的覆盖无效()
{
AssociatedObject.SelectedItemChanged-=OnTreeViewSelectedItemChanged;
base.OnDetaching();
}
私有无效OnTreeViewSelectedItemChanged(对象对象对象、RoutedPropertyChangedEventArgs参数)
{
BoundSelectedItem=args.NewValue;
}
}

有关其在my.

“show”-在绑定到TreeViewModel的视图中使用的更多详细信息?顺便说一句,您的TreeViewModel(坏名字,应该是TreeViewModel)没有SelectedItem属性我在TreeViewModel中创建了一个类似SelectedItem的属性,但如何处理它?因为选定项在TreeNode虚拟机中,所以我无法访问它。您说您有一个
SelectedItem
属性在树虚拟机中,但您无法访问它,因为它在TreeNode虚拟机中?命令在树VM中,树VM被传递给command类(该类不应该存在,但这是另一个问题),因此您可以从命令执行方法访问它。@asaf92我仍然不理解您的理想。如何从treeVM访问所选项目。我可以添加属性SelectedNode,但它如何将其引用到TReeNode vm中的选定节点我如何将根传递给第三个参数构造函数?从TreeViewModel中,我们只是通过公共TreeNode节点(TreeNode节点)运行,根节点没有参数,如果我更改此参数的签名,我得到了很多IssueTreeViewModel根,这里的根应该是TreeNode,如果是TreeNode节点,没有选择的节点。如果它是一个TreeViewModel,则没有根(我们只有公共TreeNoDeviceModel _rootNode;),您需要更改构造函数的签名,并将TreeViewModel作为依赖项传递,这样才能工作。你说的“很多问题”是什么意思?忽略命名。它应该是“树”而不是“根”,我的意思是它是从treeview模型放到构造函数中的根,这个构造函数中只有一个元素是treenode模型的根,但它是treenode,而不是treeview