I have a TreeView in my View that is databound to a list of root Nodes in my ViewModel. Those root Nodes can have child 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. That CheckBox has set IsThreeState to 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. It's been designed after this SO answer. If the set value actually changes, _SetField(ref object, object) returns true and notifies about the property change.
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. 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.
The behaviour should be the following:
- If a node's
IsSelectedgets set totrueornull, the parent node'sIsSelectedgets set tonullif not all of the node's sibling'sIsSelectedare set totrueornull(what then propagates up the tree). - If a node's
IsSelectedgets set totrueornull, the parent node'sIsSelectedgets set totrueif all of the node's sibling'sIsSelectedare set totrueornull(what then propagates up the tree). - If a node's
IsSelectedgets set tofalse, all of its immediate child node'sIsSelectedget set tofalse, too (what then propagates down the tree). - A node set to
nullmeans that not all of its immediate child nodes have been selected.
So how can I achieve firing the PropertyChanged event (or another I'd implement) only after the last node has been changed?