C# 使用HierarchycalDataTemplate的WPF MVVM树视图未更新
因此,我一直在努力让我的TreeView正确更新很长一段时间了,所以我问是否有人可以告诉我为什么我的代码没有正确更新我的TreeView节点的加法或减法。我为有点大的代码转储提前道歉,但我觉得说明这个问题非常重要 首先,我的ObservieObject类C# 使用HierarchycalDataTemplate的WPF MVVM树视图未更新,c#,wpf,xaml,mvvm,treeview,C#,Wpf,Xaml,Mvvm,Treeview,因此,我一直在努力让我的TreeView正确更新很长一段时间了,所以我问是否有人可以告诉我为什么我的代码没有正确更新我的TreeView节点的加法或减法。我为有点大的代码转储提前道歉,但我觉得说明这个问题非常重要 首先,我的ObservieObject类 public abstract class ObservableObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyCha
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
TreeNodeBase类
public abstract class TreeNodeBase : ObservableObject
{
protected const string ChildNodesPropertyName = "ChildNodes";
protected string name;
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
this.OnPropertyChanged();
}
}
protected IList<TreeNode> childNodes;
protected TreeNodeBase(string name)
{
this.Name = name;
this.childNodes = new List<TreeNode>();
}
public IEnumerable<TreeNode> ChildNodes
{
get
{
return this.childNodes;
}
}
public TreeNodeBase AddChildNode(string name)
{
var treeNode = new TreeNode(this, name);
this.childNodes.Add(treeNode);
this.OnPropertyChanged(ChildNodesPropertyName);
return treeNode;
}
public TreeNode RemoveChildNode(string name)
{
var nodeToRemove = this.childNodes.FirstOrDefault(node => node.Name.Equals(name));
if (nodeToRemove != null)
{
this.childNodes.Remove(nodeToRemove);
this.OnPropertyChanged(ChildNodesPropertyName);
}
return nodeToRemove;
}
}
public class TreeNode : TreeNodeBase
{
public TreeNodeBase Parent { get; protected set; }
public TreeNode(TreeNodeBase parent, string name)
: base(name)
{
this.Parent = parent;
}
}
TreeNode类
public class TreeNode : TreeNodeBase
{
public TreeNodeBase Parent { get; protected set; }
public TreeNode(TreeNodeBase parent, string name)
: base(name)
{
this.Parent = parent;
}
}
TreeView用户控件Xaml
<UserControl x:Class="TreeViewExperiment.TreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:treeViewExperiment="clr-namespace:TreeViewExperiment"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
d:DataContext="{d:DesignInstance treeViewExperiment:TreeViewmodel}">
<UserControl.DataContext>
<treeViewExperiment:TreeViewmodel/>
</UserControl.DataContext>
<Grid Background="White">
<Grid.Resources>
<HierarchicalDataTemplate x:Key="TreeViewHierarchicalTemplate" ItemsSource="{Binding ChildNodes}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<Style TargetType="Button">
<Setter Property="FontFamily" Value="Verdana"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="6*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TreeView Grid.Row="0" x:Name="Tree" ItemsSource="{Binding RootLevelNodes}" ItemTemplate="{StaticResource TreeViewHierarchicalTemplate}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction
Command="{Binding SetSelectedNode}"
CommandParameter="{Binding SelectedItem, ElementName=Tree}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
<Grid Grid.Row="1" Height="25">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="NameTextBox" Grid.Column="0" VerticalAlignment="Center" FontFamily="Verdana"/>
<Button Grid.Column="1" Content="Add Node" Command="{Binding AddNode}" CommandParameter="{Binding Text, ElementName=NameTextBox}" Background="Green"/>
<Button Grid.Column="2" Content="Remove Node" Command="{Binding RemoveNode}" Background="Red"/>
</Grid>
</Grid>
</UserControl>
最后是TreeViewmodel
public class TreeViewmodel : ObservableObject
{
public ICommand SetSelectedNode { get; private set; }
public ICommand AddNode { get; private set; }
public ICommand RemoveNode { get; private set; }
public TreeViewmodel()
{
this.SetSelectedNode = new ParamaterizedDelegateCommand(
node =>
{
this.SelectedTreeNode = (TreeNodeBase)node;
});
this.AddNode = new ParamaterizedDelegateCommand(name => this.SelectedTreeNode.AddChildNode((string)name));
this.RemoveNode = new DelegateCommand(
() =>
{
if (selectedTreeNode.GetType() == typeof(TreeNode))
{
var parent = ((TreeNode)this.SelectedTreeNode).Parent;
parent.RemoveChildNode(this.SelectedTreeNode.Name);
this.SelectedTreeNode = parent;
}
});
var adam = new TreeViewRoot("Adam");
var steve = adam.AddChildNode("Steve");
steve.AddChildNode("Jack");
this.rootLevelNodes = new List<TreeViewRoot> { adam, new TreeViewRoot("Eve") };
}
private TreeNodeBase selectedTreeNode;
private readonly IList<TreeViewRoot> rootLevelNodes;
public IEnumerable<TreeViewRoot> RootLevelNodes
{
get
{
return this.rootLevelNodes;
}
}
public TreeNodeBase SelectedTreeNode
{
get
{
return this.selectedTreeNode;
}
set
{
this.selectedTreeNode = value;
this.OnPropertyChanged();
}
}
}
public类TreeViewmodel:observeObject
{
public ICommand SetSelectedNode{get;private set;}
public ICommand AddNode{get;private set;}
public ICommand RemoveNode{get;private set;}
公共树视图模型()
{
this.SetSelectedNode=新的paramaterizeddelgateCommand(
节点=>
{
this.SelectedTreeNode=(TreeNodeBase)节点;
});
this.AddNode=new ParamaterizedDelegateCommand(name=>this.SelectedTreeNode.AddChildNode((字符串)name));
this.RemoveNode=新的DelegateCommand(
() =>
{
if(selectedTreeNode.GetType()==typeof(TreeNode))
{
var parent=((TreeNode)this.SelectedTreeNode).parent;
parent.RemoveChildNode(this.SelectedTreeNode.Name);
this.SelectedTreeNode=parent;
}
});
var adam=新树的树根(“adam”);
var steve=adam.AddChildNode(“steve”);
steve.AddChildNode(“杰克”);
this.rootLevelNodes=新列表{adam,new TreeViewRoot(“Eve”)};
}
私有树根据选择的树节点;
私有只读IList rootLevelNodes;
公共IEnumerable RootLevelNodes
{
得到
{
返回this.rootLevelNodes;
}
}
公共树使用选定的树节点
{
得到
{
返回此。selectedTreeNode;
}
设置
{
this.selectedTreeNode=值;
this.OnPropertyChanged();
}
}
}
因此,我知道当添加或删除子元素时,UI应该得到通知,因为当我调试它时,我可以看到在这两种情况下都调用了ChildNodes属性上的get访问器,但UI上显示的内容保持不变
在过去,我已经解决了这个问题,但是使用了ObservableCollections,这似乎是StackOverflow这类问题的大多数解决方案所指向的,但是为什么这个解决方案也不起作用呢?我遗漏了什么?问题是您误用了
INotifyPropertyChanged
。在代码中,您正在通知视图您的属性ChildNodes
已更改,但它不是真的,因为treevieItem.ItemsSource
仍然等于您的ChildNodes
属性
INotifyPropertyChanged
将在视图模型中的基础集合对象更改时导致UI更新
要在集合中出现新项目时更新ItemsSource
,需要使用实现INotifyCollectionChanged
的集合
作为:
可以枚举实现IEnumerable接口的任何集合。但是,要设置动态绑定以便集合中的插入或删除自动更新UI,集合必须实现INotifyCollectionChanged接口。此接口公开一个事件,该事件应在基础集合发生更改时引发
这就是为什么SO上的每个人都建议使用ObservableCollection
编辑:
如果要公开只读集合,则应选中。它可以作为
observateCollection
的包装,可以将其公开。这似乎就是问题所在。然而,对于我来说,远离ObservableCollection仍然很重要,因为我想向UI公开一个只读的集合。我可能会把现有的列表分解到它自己的类中,它实现了IntIfyCopyCaldC改接口,看看这是怎么回事。也许你应该考虑使用?
public class TreeViewmodel : ObservableObject
{
public ICommand SetSelectedNode { get; private set; }
public ICommand AddNode { get; private set; }
public ICommand RemoveNode { get; private set; }
public TreeViewmodel()
{
this.SetSelectedNode = new ParamaterizedDelegateCommand(
node =>
{
this.SelectedTreeNode = (TreeNodeBase)node;
});
this.AddNode = new ParamaterizedDelegateCommand(name => this.SelectedTreeNode.AddChildNode((string)name));
this.RemoveNode = new DelegateCommand(
() =>
{
if (selectedTreeNode.GetType() == typeof(TreeNode))
{
var parent = ((TreeNode)this.SelectedTreeNode).Parent;
parent.RemoveChildNode(this.SelectedTreeNode.Name);
this.SelectedTreeNode = parent;
}
});
var adam = new TreeViewRoot("Adam");
var steve = adam.AddChildNode("Steve");
steve.AddChildNode("Jack");
this.rootLevelNodes = new List<TreeViewRoot> { adam, new TreeViewRoot("Eve") };
}
private TreeNodeBase selectedTreeNode;
private readonly IList<TreeViewRoot> rootLevelNodes;
public IEnumerable<TreeViewRoot> RootLevelNodes
{
get
{
return this.rootLevelNodes;
}
}
public TreeNodeBase SelectedTreeNode
{
get
{
return this.selectedTreeNode;
}
set
{
this.selectedTreeNode = value;
this.OnPropertyChanged();
}
}
}