【发布时间】:2018-09-21 19:21:29
【问题描述】:
我的视图中有一个 TreeView,它数据绑定到我的 ViewModel 中的根 Nodes 列表。那些根Nodes 可以有子Nodes。所有节点都属于同一类型,并具有属性IsSelected,该属性绑定到CheckBox 的IsChecked 依赖属性,该属性包含在各自的TreeViewItem 中。 CheckBox 已将 IsThreeState 设置为 false。
public class Node : PropertyChangedBase, INode
{
private bool? _isSelected;
private IList<INode> _nodes;
private INode _parent;
public Node()
{ }
public bool? IsSelected
{
get { return _isSelected; }
set
{
if (_SetField(ref _isSelected, value))
{
_OnIsSelectedChanged();
}
}
}
public IList<INode> Nodes
{
get { return _nodes; }
set { _SetField(ref _nodes, value); }
}
public INode Parent
{
get { return _parent; }
set { _SetField(ref _parent, value); }
}
private void _OnIsSelectedChanged()
{
if (IsSelected.HasValue)
{
if (IsSelected.Value)
{
if (Parent != null)
{
// Set IsSelected on all parenting nodes to:
// - true, if all of their immediate child packages have been selected
// - null, else
}
if (Nodes != null && Nodes.Count > 0)
{
// Prevent running this method again by circumventing setting the property
_SetField(ref _isSelected, null);
}
}
else
{
if (Parent != null)
{
// Set IsSelected of the parent to null
}
if (Nodes != null)
{
// Set IsSelected = false on all child nodes
}
}
}
else if (Parent != null)
{
// Set IsSelected on all parenting nodes to:
// - true, if all of their immediate child packages have been selected
// - null, else
}
}
}
PropertyChangedBase 是实现INotifyPropertyChanged 的基类。它是在this SO answer 之后设计的。如果设置值实际发生变化,_SetField(ref object, object) 返回true 并通知属性变化。
如果用户单击 CheckBox,则该更改应该传播父节点的(直到根节点)IsSelected 属性和子节点的 IsSelected 属性。所有属性的传播完成后,我想触发一个事件。但仅当不再更改节点时。然后我想在 ViewModel 中做一些事情,这需要一些时间,所以如果每个更改的属性都会触发事件,那么性能会很差。
行为应该如下:
- 如果节点的
IsSelected设置为true或null,则父节点的IsSelected设置为null,如果不是所有节点的兄弟节点的IsSelected设置为true或@ 987654346@(然后向上传播)。 - 如果节点的
IsSelected设置为true或null,则父节点的IsSelected设置为true,如果该节点的所有兄弟节点的IsSelected设置为true或@9876545453 @(然后传播到树上)。 - 如果一个节点的
IsSelected设置为false,则其所有直接子节点的IsSelected也设置为false(然后沿树传播)。 - 设置为
null的节点表示尚未选择其所有直接子节点。
那么我怎样才能实现仅在最后一个节点更改后触发 PropertyChanged 事件(或我要实现的另一个事件)?
【问题讨论】:
-
我能想到的最简单的伪代码来解释你想要实现的概念是:doStuffRecursive();火灾事件();。问题是我想不出一种“检测”StuffRecursiveFinished 的方法。我会想到某种计数(知道您应该为给定请求加载多少节点),您使用 Interlocked.Decrement() 以及何时减量为 0。但我相信这会很快变得非常复杂并且有更好的通过 WPF 机制的方式
-
@AlexandruClonțea 当然。 DoStuffRecursive 然后是 fireEvent。如果我的
Node上有一个公共方法设置IsSelected而不调用_OnIsSelectedChanged,我可以在一个方法中更改所有节点,然后触发事件。不过,我必须锁定,以防止同时调用_OnIsSelectedChanged两次。我会试试的。 -
@AlexandruClonțea 谢谢你的灵感。我最终做到了这一点:提高
IsSelectedChangedPropagationStarted,执行传播,提高IsSelectedChangedPropagationCompleted。像魅力一样工作。我不敢相信,这不是我自己想出来的。有时你只需要在正确的方向上稍微推动一下。再次非常感谢。 -
很高兴听到这个消息!如果可以,请添加更完整的答案,将来可能对其他人有所帮助!一旦获得,恭喜!
标签: c# wpf mvvm event-handling treeview