ツリー構造をなすクラスを作る(その２)


[Nuget-TreeStructures](https://www.nuget.org/packages/TreeStructures/)

https://github.com/Houzkin/TreeStructure
# ツリー構造の変更通知内容
次はObservableTreeNodeCollectionを使用してみます。
```csharp
//使用するノードを定義
public class ObservableSampleNode: ObservableTreeNodeCollection<ObservableSampleNode> {
    public string Name { get; set; }
    public override string ToString() {
        return this.Name;
    }
}
```
```csharp
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;
}
```

イベント引数の説明を。

全てのノード共通で通知される情報に加え、
祖先ノードに変更があった場合 <font color="DeepPink">AncestorWasChanged は true</font> となり、AncestorInfo が格納。
同様に子孫ノードに変更があった場合は<font color="DodgerBlue"> DescendantWasChanged が true</font> となり、DescendantInfo が格納されます。

## 削除時の通知

では、上記ツリーからnode D を削除します。
```csharp
Console.WriteLine("node D を削除\n");
nodesDic["B"].RemoveChild(nodesDic["D"]);
//削除後のツリーを表示
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
```
削除前
![screenshot.3 - コピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/9a47f1ae-ea9f-880d-ce45-b1d1345e31e1.jpeg)


削除後
![screenshot.5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/e3453040-a675-a01c-b6b6-17a2b4b99599.jpeg)

出力コメント
![screenshot.4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/798c7a63-50f0-b272-6f1c-ebc765a44fe0.jpeg)


node A,B、node C,E,F,G、node D,H,I はそれぞれ同じ通知内容になっています。

**node A,B** は <font color="DodgerBlue">DescendantWasChanged が true</font> となって DescendantInfo が表示。
**node C,E,F,G** は、変更のあった node D の、祖先でも子孫でもないので AncestorWasChanged、DescendantWasChanged ともに False。
切り離された **node D,H,I** は <font color="DeepPink">AncestorWasChanged が true</font> となり AncestorInfo が表示されています

## 追加時の通知

次は先ほど削除した node D を node E の子ノードに加えます。
```csharp
Console.WriteLine("node D を node E の子ノードに追加");
nodesDic["E"].AddChild(nodesDic["D"]);
//追加後のツリーを表示
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
```
追加前
![screenshot.5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/e3453040-a675-a01c-b6b6-17a2b4b99599.jpeg)
追加後
![screenshot.7 - コピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/e1f935bd-f4a6-b5a9-396e-4542dc82e241.jpeg)

出力コメント
![screenshot.6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/45e3b54b-0e17-a983-93ac-338956e09212.jpeg)



**node A,B,E** は子孫に変更があったので <font color="DodgerBlue">DescendantWasChanged が true</font> となり、
**node C,F,G** は共にfalse、
**node D,H,I** は祖先に変更があったので <font color="DeepPink">AncestorWasChanged が true</font> です。


## 移動時の通知

今度はツリー内でノードを移動させてみます。
node E を node C の子ノードに移動。
```csharp
Console.WriteLine("node E を node C の子ノードに移動\n");
nodesDic["C"].AddChild(nodesDic["E"]);
//移動後のツリーを表示
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
```
移動前
![screenshot.7 - コピー (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/124ad734-d343-6413-03a2-5ffd15afb128.jpeg)

移動後
![screenshot.9 - コピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/4bd06196-769d-7367-c5f1-1e95d2a97972.jpeg)

出力コメント
![screenshot.8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70164/d98919fa-a7ad-fb2b-c1f5-359ab1930359.jpeg)

TreeActionとNodeActionの違いですが、
TreeActionはツリー全体から見たAction、
NodeActionは各nodeから見たSubTreeにおけるActionです。


ツリー全体(node A)から見てnode E がツリー内を移動したので TreeAction は <font color="MediumSeaGreen">Move</font>
node B から見てSubTreeから離れたので NodeAction は <font color="MediumPurple">Deviate</font>
node C から見てSubTree外から加わったので NodeAction は <font color="OrangeRed">Join</font>

ツリー内の移動なのでnode E,D,H,Iから見て <font color="Red">RootWasChanged は False</font> となります。

## 各イベントの発行順
現在のコードでは、
子ノードコレクションのイベントを購読していた場合、CollectionChangedEvent が先に発行されます。
その後、Parent プロパティの変更通知が発行され、レベルオーダーで StructureChanged イベントが発行されます。
最初のCollectionChangedが発行された時にはParentも既に設定済み、というのが理想なのでそれを実現してはいるのですが、処理過程でエラーが出た場合の処理を考えると妥協して変更した方がいいのかなぁという気もしてます。


[まだ続く](https://qiita.com/Houzkin/items/b14873aa22101984f13a)