[英]Only fire event after last TreeViewItem datasource has changed
I have a TreeView in my View that is databound to a list of root Node
s in my ViewModel. 我的View中有一个TreeView,它绑定到ViewModel中的根Node
列表。 Those root Node
s can have child Nodes
. 这些根Node
S能有子Nodes
。 All nodes are of the same type and have the property IsSelected
that is bound to the IsChecked
dependency property of a CheckBox
that's contained in the respective TreeViewItem
. 所有节点的类型相同,并具有IsSelected
属性,该属性绑定到相应TreeViewItem
包含的CheckBox
的IsChecked
依赖项属性。 That CheckBox
has set IsThreeState
to false
. 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
is a base class implementing INotifyPropertyChanged
. PropertyChangedBase
是实现INotifyPropertyChanged
的基类。 It's been designed after this SO answer . 它是根据此SO答案设计的。 If the set value actually changes, _SetField(ref object, object)
returns true
and notifies about the property change. 如果设置值实际发生更改,则_SetField(ref object, object)
返回true
并通知属性更改。
If the user clicks a CheckBox, that change should propagate the parent node's (up to the root node) IsSelected
property and to the child node's IsSelected
property, too. 如果用户单击CheckBox,则所做的更改也应将父节点的IsSelected
属性(直到根节点) IsSelected
到子节点的IsSelected
属性。 After the propagation of all Properties finished, I want to fire an event. 所有属性的传播完成后,我要触发一个事件。 But only when no further node will be changed. 但是仅当没有其他节点将被更改时。 I then want to do something in the ViewModel, that takes some time, so it would be bad performance-wise if the event would fire with each changed property. 然后,我想在ViewModel中做一些事情,这会花费一些时间,因此如果事件随每个更改的属性一起触发,则将导致性能下降。
The behaviour should be the following: 该行为应为以下情况:
IsSelected
gets set to true
or null
, the parent node's IsSelected
gets set to null
if not all of the node's sibling's IsSelected
are set to true
or null
(what then propagates up the tree). 如果一个节点的IsSelected
被设置为true
或null
,父节点的IsSelected
获取设置为null
,如果不是所有节点的兄弟姐妹的的IsSelected
被设置为true
或null
(那么什么传播了树)。 IsSelected
gets set to true
or null
, the parent node's IsSelected
gets set to true
if all of the node's sibling's IsSelected
are set to true
or null
(what then propagates up the tree). 如果节点的IsSelected
设置为true
或null
,则如果节点的所有同级节点的IsSelected
都设置为true
或null
(然后沿树传播),则父节点的IsSelected
设置为true
。 IsSelected
gets set to false
, all of its immediate child node's IsSelected
get set to false
, too (what then propagates down the tree). 如果节点的IsSelected
设置为false
,则其所有直接子节点的IsSelected
设置为false
(然后沿树传播)。 null
means that not all of its immediate child nodes have been selected. 设置为null
的节点意味着并非所有其直接子节点都已被选择。 So how can I achieve firing the PropertyChanged event (or another I'd implement) only after the last node has been changed? 那么,如何仅在更改了最后一个节点之后才能实现触发PropertyChanged事件(或我实现的另一个事件)?
I ended up doing what Alexandru suggested. 我最终按照亚历山大的建议做了。
I introduced two events IsSelectedChangedPropagationStarted
& IsSelectedChangedPropagationCompleted
the first being raised before handling the selection and the latter being raised upon completion. 我介绍了两个事件IsSelectedChangedPropagationStarted
和IsSelectedChangedPropagationCompleted
,第一个事件在处理选择之前IsSelectedChangedPropagationCompleted
,第二个事件在完成选择时引发。 The class looks similar to this now: 该类现在看起来与此类似:
public class Node : PropertyChangedBase, INode
{
// #### Attributes
private bool? _isSelected;
private IList<INode> _nodes;
private INode _parent;
// #### Constructor
public Node()
{ }
// #### Properties
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); }
}
// #### Events
public event EventHandler IsSelectedChangedPropagationStarted;
public event EventHandler IsSelectedChangedPropagationCompleted;
// #### Instance Methods
private void _OnIsSelectedChanged()
{
IsSelectedChangedPropagationStarted?.Invoke(this, EventArgs.Empty);
if (IsSelected.HasValue)
{
if (IsSelected.Value)
{
RecursivelySetAllParents();
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
}
RecursivelySetAllChildren();
}
}
else if (Parent != null)
{
// Set IsSelected on all parenting nodes to:
// - true, if all of their immediate child packages have been selected
// - null, else
}
IsSelectedChangedPropagationCompleted?.Invoke(this, EventArgs.Empty);
}
}
By implementing custom EventArgs
I could also tell the listeners if something at all has changed so they can act accordingly. 通过实现自定义EventArgs
我还可以告诉侦听器是否有任何更改,以便他们可以采取相应的措施。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.