ツリー構造の変更通知内容
次はObservableTreeNodeCollectionを使用してみます。
//使用するノードを定義
public class ObservableSampleNode: ObservableTreeNodeCollection<ObservableSampleNode> {
public string Name { get; set; }
public override string ToString() {
return this.Name;
}
}
Console.WriteLine("コレクションをN分木として組み立てる");
var nodesDic = "ABCDEFGHIJ".ToCharArray().Select(x => x.ToString())
.ToDictionary(x => x, x => new ObservableSampleNode() { Name = x });
var root = nodesDic.Values.AssembleAsNAryTree(3);
//ツリーを表示
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
EventHandler<StructureChangedEventArgs<ObservableSampleNode>> structreChangedHdlr = (s, e) => {
Console.WriteLine($"sender:{s} \nTarget:{e.Target} TreeAction:{e.TreeAction} PreviousParentOfTarget:{e.PreviousParentOfTarget} OldIndex:{e.OldIndex} AncestorWasChanged:{e.AncestorWasChanged} DescendantWasChanged:{e.DescendantWasChanged}");
if (e.AncestorWasChanged) {
//祖先ノードに変更があった場合の情報を表示
var info = e.AncestorInfo!;
Console.WriteLine($"MovedTarget:{info.MovedTarget} OldIndex:{info.OldIndex} PreviousParentOfTarget:{info.PreviousParentOfTarget} RootWasChanged:{info.RootWasChanged}");
}
if (e.DescendantWasChanged) {
//子孫ノードに変更があった場合の情報を表示
var info = e.DescendantInfo!;
Console.WriteLine($"Target:{info.Target} NodeAction:{info.NodeAction} OldIndex:{info.OldIndex} PreviousParentOfTarget:{info.PreviousParentOfTarget}");
}
Console.Write("\n");
};
PropertyChangedEventHandler propertyChangedHdlr = (s, e) => { Console.WriteLine($"sender:{s} Parent Changed.\n"); };
foreach (var node in root.Preorder()) {
node.StructureChanged += structreChangedHdlr;
node.PropertyChanged += propertyChangedHdlr;
}
イベント引数の説明を。
全てのノード共通で通知される情報に加え、
祖先ノードに変更があった場合 AncestorWasChanged は true となり、AncestorInfo が格納。
同様に子孫ノードに変更があった場合は DescendantWasChanged が true となり、DescendantInfo が格納されます。
削除時の通知
では、上記ツリーからnode D を削除します。
Console.WriteLine("node D を削除\n");
nodesDic["B"].RemoveChild(nodesDic["D"]);
//削除後のツリーを表示
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
node A,B、node C,E,F,G、node D,H,I はそれぞれ同じ通知内容になっています。
node A,B は DescendantWasChanged が true となって DescendantInfo が表示。
node C,E,F,G は、変更のあった node D の、祖先でも子孫でもないので AncestorWasChanged、DescendantWasChanged ともに False。
切り離された node D,H,I は AncestorWasChanged が true となり AncestorInfo が表示されています
追加時の通知
次は先ほど削除した node D を node E の子ノードに加えます。
Console.WriteLine("node D を node E の子ノードに追加");
nodesDic["E"].AddChild(nodesDic["D"]);
//追加後のツリーを表示
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
node A,B,E は子孫に変更があったので DescendantWasChanged が true となり、
node C,F,G は共にfalse、
node D,H,I は祖先に変更があったので AncestorWasChanged が true です。
移動時の通知
今度はツリー内でノードを移動させてみます。
node E を node C の子ノードに移動。
Console.WriteLine("node E を node C の子ノードに移動\n");
nodesDic["C"].AddChild(nodesDic["E"]);
//移動後のツリーを表示
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
TreeActionとNodeActionの違いですが、
TreeActionはツリー全体から見たAction、
NodeActionは各nodeから見たSubTreeにおけるActionです。
ツリー全体(node A)から見てnode E がツリー内を移動したので TreeAction は Move
node B から見てSubTreeから離れたので NodeAction は Deviate
node C から見てSubTree外から加わったので NodeAction は Join
ツリー内の移動なのでnode E,D,H,Iから見て RootWasChanged は False となります。
各イベントの発行順
現在のコードでは、
子ノードコレクションのイベントを購読していた場合、CollectionChangedEvent が先に発行されます。
その後、Parent プロパティの変更通知が発行され、レベルオーダーで StructureChanged イベントが発行されます。
最初のCollectionChangedが発行された時にはParentも既に設定済み、というのが理想なのでそれを実現してはいるのですが、処理過程でエラーが出た場合の処理を考えると妥協して変更した方がいいのかなぁという気もしてます。