-
Notifications
You must be signed in to change notification settings - Fork 0
TreeNodeBase_ja
ツリーノードを表すインターフェースとして以下を定義しています。
/// <summary>Represents a node that forms a tree structure.</summary>
/// <typeparam name="TNode">The common base type for each node.</typeparam>
public interface ITreeNode<TNode> where TNode : ITreeNode<TNode> {
/// <summary>Gets the parent node.</summary>
TNode? Parent { get; }
/// <summary>Gets the child nodes.</summary>
IEnumerable<TNode> Children { get; }
}
/// <summary>Defines a mutable tree structure.</summary>
/// <typeparam name="TNode">The type of the nodes.</typeparam>
public interface IMutableTreeNode<TNode> : ITreeNode<TNode>
where TNode : IMutableTreeNode<TNode> {
/// <summary>Adds a child node.</summary>
/// <param name="child">The child node.</param>
/// <returns>The current node.</returns>
TNode AddChild(TNode child);
/// <summary>Adds a child node at the specified index.</summary>
/// <param name="index">The index.</param>
/// <param name="child">The child node.</param>
/// <returns>The current node.</returns>
TNode InsertChild(int index, TNode child);
/// <summary>Removes a child node.</summary>
/// <param name="child">The child node to remove.</param>
/// <returns>The removed node.</returns>
TNode RemoveChild(TNode child);
/// <summary>Removes all child nodes.</summary>
/// <returns>The removed nodes.</returns>
IReadOnlyList<TNode> ClearChildren();
}
/// <summary>Represents an observable tree structure.</summary>
/// <typeparam name="TNode">The type of the nodes.</typeparam>
public interface IObservableTreeNode<TNode> : ITreeNode<TNode>
where TNode : IObservableTreeNode<TNode> {
/// <summary>Called by <see cref="StructureChangedEventExecutor{TNode}"/> when the tree structure changes.</summary>
/// <param name="e"></param>
void OnStructureChanged(StructureChangedEventArgs<TNode> e);
/// <summary>
/// Notification event for when the tree structure changes.
/// </summary>
event EventHandler<StructureChangedEventArgs<TNode>>? StructureChanged;
}ITreeNodeで定義されているプロパティのみがpublicで公開されている、ReadOnlyな状態です。
派生先で必要なメソッドを公開して使用します。
public abstract class TreeNodeBase<TNode> : ITreeNode<TNode>
where TNode : TreeNodeBase<TNode>, ITreeNode<TNode>{
protected IEnumerable<TNode> ChildNodes { get; }
protected abstract IEnumerable<TNode> SetupInnerChildCollection();
protected virtual IEnumerable<TNode> SetupPublicChildCollection(IEnumerable<TNode> innerCollection);
protected virtual bool CanAddChildNode(TNode child);
protected virtual void InsertChildProcess(int index, TNode child, Action<IEnumerable<TNode>, int, TNode>? action = null);
protected virtual void RemoveChildProcess(TNode child, Action<IEnumerable<TNode>, TNode>? action = null);
protected virtual void SetChildProcess(int index, TNode child, Action<IEnumerable<TNode>, int, TNode>? action = null);
protected virtual void ClearChildProcess(Action<IEnumerable<TNode>>? action = null);
protected virtual void ShiftChildProcess(int oldIndex, int newIndex, Action<IEnumerable<TNode>, int, int>? action = null);
protected virtual void DisposeProcess();
}SetupInnerChildCollectionで指定したコレクションが、ChildNodesプロパティとして設定されます。
ChildNodesプロパティは内部処理で直接操作されるコレクションです。
ChildNodesを引数にとるSetupPublicChildCollectionで指定したコレクションが外部公開用のコレクションとして設定されます。
内部処理用のコレクションと外部公開用のコレクションをそれぞれ設定することで、外部からのコレクション操作を防止しています。
ObservableGeneralTreeNodeでは以下のように設定しています。
/// <inheritdoc/>
protected override IEnumerable<TNode> SetupInnerChildCollection() => new ObservableCollection<TNode>();
/// <inheritdoc/>
protected override IEnumerable<TNode> SetupPublicChildCollection(IEnumerable<TNode> innerCollection)
=> new ReadOnlyObservableCollection<TNode>((innerCollection as ObservableCollection<TNode>)!);CanAddChildNodeメソッドではノードの循環と兄弟ノードとの重複をチェックし、指定されたノードが追加可能かどうか示します。
InsertChildProcess, SetChildProcessの処理過程で呼び出されます。
InsertChildProcess, RemoveChildProcess, SetChildProcess, ClearChildProcess, ShiftChildProcess, DisposeProcessの6つのメソッドで、
親ノードと子ノードが互いのProcessメソッドを呼び合うことで相互参照を実現しています。
実際のコレクション操作は、引数actionで指定可能で、null指定でデフォルトの操作が実行されます。(デフォルトの操作はXMLコメントに記載)
ChildNodesをIList<TNode>やICollection<TNode>へキャストしているので、もしSetupInnerChildCollectionで指定したコレクションがそれらのインターフェイスを実装していない場合は、各メソッドの引数actionを指定する必要があります。