ReactivePropertyを利用してUndo可能なアプリケーションを作成する

#初めに
***ReactiveProperty***（とRx）を利用して、Undo可能なアプリケーションを作成してみました。

ソースコードは↓こちら
https://github.com/dyoneda/UndoProject

#Redo/Undo
Redo/Undoを実現するために、Redo処理/Undo処理を保持するActionをPropertyとするクラスを用意します。

```csharp:UndoCommand.cs
public class UndoCommand
{
    public UndoCommand(Action undo, Action redo)
    {
        this.UndoAction = undo;
        this.RedoAction = redo;
    }

    public Action UndoAction { get; }
    public Action RedoAction { get; }
}
```
使い方はこんな感じ

```csharp
//// こんなクラスがあるとして
//class Hoge { public string Fuga { get; set; }}

// どこかから対象オブジェクトを取得
var hoge = GetHoge();
// 変更前の値を保持
var oldValue = hoge.Fuga;
// これから変更する値。
var newValue = "new";

// Redo/Undo用のオブジェクトを生成
var command = new UndoCommand(() => hoge.Fuga = oldValue, () => hoge.Fuga = new Vlaue);

// 適用
command.Redo();
// 元に戻す
command.Undo();
// 再適用
command.Redo();
```

でも、これだけは処理が煩雑になるため、UndoCommandクラスを管理するためのクラスも用意します。Redo/UndoはFILOであるためStackクラスを利用するといい感じです。

```csharp:UndoCommand.cs
public class UndoStack
{
    private readonly Stack<UndoCommand> _undoStack = new Stack<UndoCommand>();
    private readonly Stack<UndoCommand> _redoStack = new Stack<UndoCommand>();

    public static UndoStack Instance { get; } = new UndoStack();

    public void Push(UndoCommand command)
    {
        this._redoStack.Clear();
        this._undoStack.Push(command);
        command.RedoAction();
    }

    public bool CanUndo() => this._undoStack.Any();
    public bool CanRedo() => this._redoStack.Any();

    public void Undo()
    {
        if (!this.CanUndo()) return;
        var command = this._undoStack.Pop();
        command.UndoAction();
        this._redoStack.Push(command);
    }

    public void Redo()
    {
        if (!this.CanRedo()) return;
        var command = this._redoStack.Pop();
        command.RedoAction();
        this._undoStack.Push(command);
    }
}
```

#ReactivePropertyによるVM/Mの同期
VM/M間の同期の間に先ほどのRedo/Undoの仕組みを組み込んでしまえばほぼほぼできたようなものです。
ReactivePropertyでVM/Mの同期といえば、***ToReactivePropertyAsSynchronized***メソッドなのですが、ここでは挙動を柔軟にカスタマイズするために***ToReactiveProperty***/***Subscribe***の組み合わせで対応したいと思います。
（参考）https://code.msdn.microsoft.com/windowsdesktop/ReactiveProperty-085c3090

```csharp:ViewModel.cs
private ReactiveProperty<TProp> CreateReactiveProperty<TObj, TProp>(TObj obj, Expression<Func<TObj, TProp>> exp) 
    where TObj : INotifyPropertyChanged
{
    var latest = default(TProp);
    var rp = obj.ObserveProperty(exp).Do(v => latest = v).ToReactiveProperty().AddTo(this._disposable);
    rp.Where(v => !object.Equals(v, latest)).Subscribe(v =>
    {
        UndoStack.Instance.Push(UndoCommand.CreateCommand(obj, exp, v));
        this._subjects.OnNext(true);
    }).AddTo(this._disposable);
    return rp;
}
```

上記のような便利メソッドをひとつ定義しておけば、下記のように使いまわせます。

```csharp:ViewModel.cs
this.StringProperty = CreateReactiveProperty(m, o => o.StringValue);
this.IntProperty = CreateReactiveProperty(m, o => o.IntValue);
this.BoolProperty = CreateReactiveProperty(m, o => o.BoolValue);
```

あとは、別件ですが***ToReactiveProperty***や***Subscribe***は、神経質にDisposeするぐらいでちょうどいいと思います。
Rxが提供する***CompositeDisposable***が便利なのでぜひ利用しましょう。

#最後に
今回はReactiveProeprtyのみNuGetから取得した形＆Qiita用やっつけサンプルなのでべた書きな部分が多いですが、その他MVVM系ライブラリを利用するとスマートに書ける部分が多いので、検討してみてくだい。
例えば、
・INotifyPropertyChanged
・Propertyの動的取得
・WindowClose時にVMをDispose
・Undo/RedoボタンのEnableのIObservable化
あたりですね。