かずきのBlog@hatena

日本マイクロソフトに勤めています。このブログは個人ブログなので、ここに書いている内容は個人的な意見で会社の公式見解ではない点にご注意ください。好きなものは XAML と C# 。苦手なものは型の無い言語です。

ReactivePropertyで、ViewModelのプロパティに変更があったかどうか判別できるようにしたい

プロパティの値が1つでも書き換わってたら判別したい。そういうことはありますよね? ReactivePropertyを使うと、こんな感じで実現できます。(変更があったかというフラグをリセットする機能つき)

まず、ReactivePropertyに変更があったときにtrueを発行するIObservableに変換するメソッドを作っておきます。

static class MvvmHelpersExtensions
{
    public static IObservable<bool> ChangedAsObservable<T>(this ReactiveProperty<T> self, bool skipFirst = true)
    {
        var result = self.AsObservable();
        if (skipFirst)
        {
            result = result.Skip(1);
        }
        return result.Select(_ => true);
    }
}

このメソッドを使うと、ReactivePropertyに変更があったときにtrueがくるので、必要なプロパティの数だけMergeしてReadOnlyReactiveProperty(初期値false)あたりにしてしまえばOKです。そのとき、Subjectを渡して任意の値にセットできるようにしておくと、リセットにも対応できます。

class MountainViewModel
{
    private Subject<bool> ResetIsDirtySubject { get; } = new Subject<bool>();
    public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
    public ReactiveProperty<double> HeightInMeters { get; } = new ReactiveProperty<double>();
    public ReadOnlyReactiveProperty<bool> IsDirty { get; }

    public MountainViewModel()
    {
        this.IsDirty = Observable.Merge(
            this.Name.ChangedAsObservable(),
            this.HeightInMeters.ChangedAsObservable(),
            this.ResetIsDirtySubject)
            .ToReadOnlyReactiveProperty(false);
    }

    public void ResetDirty()
    {
        this.ResetIsDirtySubject.OnNext(false);
    }
}