using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; namespace WPFluent.Controls; public class TreeModelNode : ITreeNode, INotifyPropertyChanged { private class NodeCollection(TreeModelNode owner) : Collection { private TreeModelNode? _owner = owner; protected override void ClearItems() { while (Count != 0) { RemoveAt(Count - 1); } } protected override void InsertItem(int index, TreeModelNode item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } if (item.Parent != _owner) { item.Parent?.Children.Remove(item); item._parent = _owner; item._index = index; for (int i = index; i < Count; i++) { this[i]._index++; } base.InsertItem(index, item); } } protected override void RemoveItem(int index) { TreeModelNode item = this[index]; item._parent = null!; item._index = -1; for (int i = index + 1; i < Count; i++) { this[i]._index--; } base.RemoveItem(index); } protected override void SetItem(int index, TreeModelNode item) { _ = item ?? throw new ArgumentNullException(nameof(item)); RemoveAt(index); InsertItem(index, item); } } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged(string name) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } private TreeModelListView _tree; public TreeModelListView Tree => _tree; private INotifyCollectionChanged _childrenSource = null!; internal INotifyCollectionChanged ChildrenSource { get => _childrenSource; set { if (_childrenSource != null) { _childrenSource.CollectionChanged -= ChildrenChanged; } _childrenSource = value; if (_childrenSource != null) { _childrenSource.CollectionChanged += ChildrenChanged; } } } private int _index = -1; public int Index => _index; /// /// Returns true if all parent nodes of this node are expanded. /// internal bool IsVisible { get { TreeModelNode? node = _parent; while (node != null) { if (!node.IsExpanded) { return false; } node = node.Parent; } return true; } } public bool IsExpandedOnce { get; internal set; } public bool HasChildren { get; internal set; } private bool _isExpanded; public bool IsExpanded { get => _isExpanded; set { if (value != IsExpanded) { Tree.SetIsExpanded(this, value); OnPropertyChanged(nameof(IsExpanded)); OnPropertyChanged(nameof(IsExpandable)); } } } internal void AssignIsExpanded(bool value) { _isExpanded = value; } public bool IsExpandable => (HasChildren && !IsExpandedOnce) || Nodes.Count > 0; private bool _isSelected; public bool IsSelected { get => _isSelected; set { if (value != _isSelected) { _isSelected = value; OnPropertyChanged(nameof(IsSelected)); } } } private TreeModelNode? _parent = null; public TreeModelNode? Parent => _parent; public int Level => _parent == null ? -1 : _parent.Level + 1; public TreeModelNode? PreviousNode { get { if (_parent != null) { int index = Index; if (index > 0) { return _parent.Nodes[index - 1]; } } return null; } } public TreeModelNode? NextNode { get { if (_parent != null) { int index = Index; if (index < _parent.Nodes.Count - 1) { return _parent.Nodes[index + 1]; } } return null; } } internal TreeModelNode? BottomNode { get { if (Parent != null) { return Parent.NextNode ?? Parent.BottomNode; } return null; } } internal TreeModelNode? NextVisibleNode { get { if (IsExpanded && Nodes.Count > 0) { return Nodes[0]; } else { return NextNode ?? BottomNode; } } } public int VisibleChildrenCount => AllVisibleChildren.Count(); public IEnumerable AllVisibleChildren { get { int level = this.Level; TreeModelNode? node = this; while (true) { node = node.NextVisibleNode; if (node != null && node.Level > level) { yield return node; } else { break; } } } } private object? _content = null; public object? Content => _content; private Collection _children = null!; internal Collection Children => _children; private ReadOnlyCollection _nodes = null!; public ReadOnlyCollection Nodes => _nodes; internal TreeModelNode(TreeModelListView tree, object? content) { _ = tree ?? throw new ArgumentNullException(nameof(tree)); _tree = tree; _children = new NodeCollection(this); _nodes = new ReadOnlyCollection(_children); _content = content; } public override string ToString() { return Content != null ? Content.ToString()! : base.ToString()!; } private void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: if (e.NewItems != null) { int index = e.NewStartingIndex; int rowIndex = Tree.Rows.IndexOf(this); foreach (object obj in e.NewItems) { Tree.InsertNewNode(this, obj, rowIndex, index); index++; } } break; case NotifyCollectionChangedAction.Remove: if (Children.Count > e.OldStartingIndex) { RemoveChildAt(e.OldStartingIndex); } break; case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Reset: while (Children.Count > 0) { RemoveChildAt(0); } Tree.CreateChildrenNodes(this); break; } HasChildren = Children.Count > 0; OnPropertyChanged(nameof(IsExpandable)); } private void RemoveChildAt(int index) { var child = Children[index]; Tree.DropChildrenRows(child, true); ClearChildrenSource(child); Children.RemoveAt(index); } private void ClearChildrenSource(TreeModelNode node) { node.ChildrenSource = null!; foreach (TreeModelNode n in node.Children) { ClearChildrenSource(n); } } } public interface ITreeNode { public TreeModelListView Tree { get; } public bool IsExpanded { get; set; } public bool IsExpandable { get; } public bool IsSelected { get; set; } public int Level { get; } public object? Content { get; } }