ReactivePropertyの便利メソッド(ToReactivePropertyとToReactivePropertyAsSynchronized)
名前が長くて呼び出し方をよく忘れてしまいますが、ViewModelとModelで片方向・双方向バインドができてとても便利です
なるべく短くまとめたいと思います
ToReactiveProperty系メソッド
- ToReactiveProperty
- ToReadOnlyReactiveProperty
- ToReadOnlyReactivePropertySlim
Modelの値が変更されたとき、ViewModelへの通知を行います(片方向)
こんな感じでボタンを押すとTextBlockに値のカウントを増やす動きを作ります
まずはサンプルコード
- ViewのButtonとTextBlock
<Button Width="100" Height="40" Content="Button" Command="{Binding ButtonCommand}" FontSize="20"/> <TextBlock Text="{Binding Counter.Value}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="50"/>
- ModelとViewModel
public class Model : BindableBase { private int pushCount; public int PushCount { get { return pushCount; } set { SetProperty(ref pushCount, value); } } } public class MainWindowViewModel : BindableBase { public ReactiveCommand ButtonCommand { get; } = new ReactiveCommand(); public ReadOnlyReactivePropertySlim<int> Counter { get; } public MainWindowViewModel() { var model = new Model(); this.ButtonCommand.Subscribe(() => { model.PushCount++; }); this.Counter = model.ObserveProperty(x => x.PushCount).ToReadOnlyReactivePropertySlim(); } }
- 大事なポイント
this.PushCount = model.ObserveProperty(x => x.PushCount).ToReadOnlyReactivePropertySlim();
ModelはINotifyChangedを継承する必要があります(BindableBaseはINotifyChangedを継承したPrismのクラス)
ObservePropertyから変更通知を受け取りたいプロパティを指定してToReactivePropertyします
こうすることで
- ボタンが押されたらModel.PushCountの値を変更
- Model.PushCountがViewModelCounterへ変更を通知
- 表示の更新
という動きが実現できます
ToReactivePropertyAsSynchronized
- ViewModelの値が変更された時、Modelに変更の通知
- Modelの値が変更された時、ViewModelに変更の通知
を行います(双方向)
サンプルコード
- Viewのそれぞれ
<TextBox Width="200" Height="40" Text="{Binding Input.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="20"/> <TextBlock Text="{Binding Output.Value}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="40"/> <Button Width="100" Height="40" Content="VM" Command="{Binding ViewModelChangeCommand}" FontSize="20"/> <Button Width="100" Height="40" Content="M" Command="{Binding ModelChangeCommand}" FontSize="20"/>
- ModelとViewModel
public class Model : BindableBase { private string input; public string Input { get { return input; } set { SetProperty(ref input, value); } } } public class MainWindowViewModel : BindableBase { public ReactiveProperty<string> Input { get; } public ReadOnlyReactivePropertySlim<string> Output { get; } public ReactiveCommand ViewModelChangeCommand { get; } = new ReactiveCommand(); public ReactiveCommand ModelChangeCommand { get; } = new ReactiveCommand(); public MainWindowViewModel() { var model = new Model(); this.Input = model.ToReactivePropertyAsSynchronized(x => x.Input); this.Output = model.ObserveProperty(x => x.Input).ToReadOnlyReactivePropertySlim(); this.ModelChangeCommand.Subscribe(() => { model.Input = "M"; }); this.ViewModelChangeCommand.Subscribe(() => { this.Input.Value = "VM"; }); } }
- 大事なポイント
this.Input = model.ToReactivePropertyAsSynchronized(x => x.Input);
INotifyChangedを継承したModelからToReactivePropertyAsSynchronizedを呼び出し、双方向バインドしたいプロパティを指定します
こうすることで
- TextBoxに文字を入力するとViewModel.Inputの値が変更
- ViewModel.InputがModel.Inputに変更を通知
- Model.InputがViewModel.Outputに変更を通知(表示の更新)
という動きができています。さらにボタンを押したときには
Mボタンの場合
ボタンが押されたらModel.Inputの値を変更
Model.InputがViewModel.InputとViewModel.Outputに変更を通知(TextBoxとTextBlockの表示更新)
VMボタンの場合
ボタンが押されたらViewModel.Inputの値を変更(TextBoxの表示更新)
- ViewModel.InputがModel.Inputに変更を通知
- Model.InputがViewModel.Outputに変更を通知(TextBlockの表示の更新)
という動きになります
わざわざモデルの値を見に行く必要がなくなるので便利