読者です 読者をやめる 読者になる 読者になる

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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);
    }
}