简体   繁体   English

最后一个TreeViewItem数据源更改后的仅触发事件

[英]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包含的CheckBoxIsChecked依赖项属性。 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: 该行为应为以下情况:

  • If a node's 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被设置为truenull ,父节点的IsSelected获取设置为null ,如果不是所有节点的兄弟姐妹的的IsSelected被设置为truenull (那么什么传播了树)。
  • If a node's 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设置为truenull ,则如果节点的所有同级节点的IsSelected都设置为truenull (然后沿树传播),则父节点的IsSelected设置为true
  • If a node's 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 (然后沿树传播)。
  • A node set to 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. 我介绍了两个事件IsSelectedChangedPropagationStartedIsSelectedChangedPropagationCompleted ,第一个事件在处理选择之前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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM