BindableWrapperSample_ja - Houzkin/TreeStructures GitHub Wiki
HierarchyWrapperとBindableHierarchyWrapperの違いは以下です。
| Wrapper \ 動作 | Dispose | 子ノードコレクションの変更通知 |
|---|---|---|
| HierarchyWrapper | 定義なし | Childrenプロパティ呼び出し時 |
| BindableHierarchyWrapper | 対象と子孫 | 随時 |
BindableWrapperはDisposeされることで子孫ノードも含めてDisposeされます。
また、HandleRemovedChildメソッドをoverrideしており、削除された子ノードに対してもDisposeメソッドを呼び出しています。
これにより、派生先でリソースの開放が必要な場合でも、protected void Dispose(bool disposing)をoverrideすることで対処可能です。
DisposeされたWrapperノードは全て分解された状態になります。 以下、Wrapperの比較。
public interface IMemberNode {
string MemberName { get; }
int Followers { get; }
}
public class ObservableMemberNode : ObservableGeneralTreeNode<ObservableMemberNode>, IMemberNode {
public ObservableMemberNode(string name){
MemberName = name;
this.StructureChanged += Member_StructureChanged;
}
private void Member_StructureChanged(object? sender, StructureChangedEventArgs<ObservableMemberNode> e) {
if(e.IsDescendantChanged && e.DescendantInfo!.SubTreeAction != TreeNodeChangedAction.Move){
this.RaisePropertyChanged(nameof(Followers));
}
}
public string MemberName { get; }
public int Followers => this.Preorder().Count() - 1;
}上記クラスを定義したうえで、WrapperクラスとBindableWrapperクラスの挙動を比較してみます。
public class MemberWrapper<TSrc> : TreeNodeWrapper<TSrc,MemberWrapper<TSrc>> where TSrc:class,ITreeNode<TSrc>,IMemberNode{
public MemberWrapper(TSrc member) : base(member) { }
protected override MemberWrapper<TSrc> GenerateChild(TSrc sourceChildNode) {
return new MemberWrapper<TSrc>(sourceChildNode);
}
public string MemberName=>Source.MemberName;
public int Followers => Source.Followers;
public int WrappingFollowers => this.Preorder().Count() - 1;
}
public class BindableMemberWrapper<TSrc>:BindableTreeNodeWrapper<TSrc,BindableMemberWrapper<TSrc>>
where TSrc:class, ITreeNode<TSrc>,IMemberNode{
public BindableMemberWrapper(TSrc member):base(member){ }
protected override BindableMemberWrapper<TSrc> GenerateChild(TSrc sourceChildNode) {
return new BindableMemberWrapper<TSrc>(sourceChildNode);
}
public string MemberName => Source.MemberName;
public int Followers => Source.Followers;
public int WrappingFollowers => this.Preorder().Count() - 1;
}
internal class SampleE {
public static void Method(){
var members = "ABCDEFGHIJ".ToCharArray().Select(x=>x.ToString()).ToDictionary(x => x, x => new ObservableMemberNode(x));
var memberRoot = members.Values.AssembleAsNAryTree(2);
var wrpMemberRt = new MemberWrapper<ObservableMemberNode>(memberRoot);
var dispoWrpMemberRt = new BindableMemberWrapper<ObservableMemberNode>(memberRoot);
Console.WriteLine("initial state of Wrapper");
Console.WriteLine(wrpMemberRt.ToTreeDiagram(x => $"name:{x.MemberName}, Followers:{x.Followers}"));
Console.WriteLine("initial state of BindableWrapper.");
Console.WriteLine(dispoWrpMemberRt.ToTreeDiagram(x => $"name:{x.MemberName}, Followers:{x.Followers}, IsDisposed:{x.IsDisposed}"));
var wrpB = wrpMemberRt.Preorder().First(x => x.MemberName == "B");
var dspWrpB = dispoWrpMemberRt.Preorder().First(x=> x.MemberName == "B");
Console.WriteLine("remove node B\n");
memberRoot.Levelorder().First(x => x.MemberName == "B").TryRemoveOwn();
Console.WriteLine("each root wrappers");
Console.WriteLine(wrpMemberRt.ToTreeDiagram(x => $"name:{x.MemberName}, Followers:{x.Followers}"));
Console.WriteLine(dispoWrpMemberRt.ToTreeDiagram(x => $"name:{x.MemberName}, Followers:{x.Followers}, IsDisposed:{x.IsDisposed}"));
Console.WriteLine("each nodeB wrappers");
Console.WriteLine(wrpB.ToTreeDiagram(x => $"name:{x.MemberName}, Followers:{x.Followers}"));
Console.WriteLine(dspWrpB.ToTreeDiagram(x => $"name:{x.MemberName}, Followers:{x.Followers}, IsDisposed:{x.IsDisposed}"));
Console.WriteLine("Dispose nodeC wrapper");
dispoWrpMemberRt.Preorder().First(x => x.MemberName == "C").Dispose();
Console.WriteLine(dispoWrpMemberRt.ToTreeDiagram(x => $"name:{x.MemberName}, Followers:{x.Followers}, IsDisposed:{x.IsDisposed}"));
}
}

破棄されたWrapperノードはソースとの連動も停止します。
HierarchyWrapper.Childrenの変更は、プロパティ取得時に更新されます。
BindableHiererchyWrapper.Childrenプロパティは変更を即時反映します。
var tree = "ABCDEFG".ToCharArray().Select(x => x.ToString()).AssembleAsNAryTree(2,
x => new ObservableOtherHierarchy(x) as OtherHierarchy,
x => x.Nests,
(p, c) => p.Nests.Add(c));
var Wrpr = new OtherHierarchyWrapper(tree);
var BWrpr = new BindableOtherHierarchyWrapper(tree);
((INotifyCollectionChanged)Wrpr.Children).CollectionChanged += (s, e) => Console.WriteLine("Wrpr.Children collection changed");
((INotifyCollectionChanged)BWrpr.Children).CollectionChanged += (s, e) => Console.WriteLine("BWrpr.Children collection changed");
tree.Nests.Add(new OtherHierarchy("H"));
//BWrpr.Children collection changed
Console.WriteLine(BWrpr.ToTreeDiagram(x => x.SourceName));
/*
A
├ B
│ ├ D
│ └ E
├ C
│ ├ F
│ └ G
└ H
* */
Console.WriteLine(Wrpr.ToTreeDiagram(x => x.SourceName));
/*
//Wrpr.Children collection changed
A
├ B
│ ├ D
│ └ E
├ C
│ ├ F
│ └ G
└ H
* */