かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

ReactivePropertyでViewModelに紐づくModelを差し替えるときのパターン

という呟きを見つけたのでどういう風にやるのかをちょろっと。

Modelのプロパティの変更を監視してViewModelを作り直す

こんなPersonクラスがあるとします。

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private static readonly PropertyChangedEventArgs NamePropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(Name));

    private string name;

    public string Name
    {
        get { return this.name; }
        set
        {
            if (this.name == value) { return; }
            this.name = value;
            this.PropertyChanged?.Invoke(this, NamePropertyChangedEventArgs);
        }
    }
}

そして、それを保持するAppModelとかいうクラスがいるとします。

public class AppModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private static readonly PropertyChangedEventArgs PersonPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(Person));

    private Person person;

    public Person Person
    {
        get { return this.person; }
        set
        {
            if (this.person == value) { return; }
            this.person = value;
            this.PropertyChanged?.Invoke(this, PersonPropertyChangedEventArgs);
        }
    }

    public void ChangePerson()
    {
        this.Person = new Person();
    }
}

ChangePersonでPersonクラスのインスタンスを差し替えるっていうイメージ。

んで、Personに紐づくVM。

public class PersonViewModel : IDisposable
{
    public ReactiveProperty<string> Name { get; private set; }
    public PersonViewModel(Person model)
    {
        this.Name = model.ToReactivePropertyAsSynchronized(x => x.Name);
    }
    public void Dispose() { this.Name.Dispose(); }
}

そして、PersonViewModelを持ってるMainPageViewModelがこんな感じ。AppModelのPersonの変更を監視して、PersonViewModelを作り直しています。

public class MainPageViewModel
{
    private AppModel model = new AppModel();

    public ReadOnlyReactiveProperty<PersonViewModel> Person { get; private set; }

    public MainPageViewModel()
    {
        this.Person = model
            .ObserveProperty(x => x.Person)
            .Do(_ => this.Person?.Dispose())
            .Select(x => new PersonViewModel(x))
            .ToReadOnlyReactiveProperty();
    }
}

PersonViewModelを作り直したくない

そんな時は、悲しいけどPersonViewModelにINotifyPropertyChangedを実装して、ReactiveProperty型のプロパティの変更通知を実装しておきます。そうするとこんな感じでModelの差し替えができます。

public class PersonViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private static readonly PropertyChangedEventArgs NamePropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(Name));

    private ReactiveProperty<string> name;

    public ReactiveProperty<string> Name
    {
        get { return this.name; }
        set
        {
            if (this.name == value) { return; }
            this.name?.Dispose(); // 直前のModelとの接続を切断
            this.name = value;
            this.PropertyChanged?.Invoke(this, NamePropertyChangedEventArgs);
        }
    }

    public void SetModel(Person p)
    {
        this.Name = p.ToReactivePropertyAsSynchronized(x => x.Name);
    }
}