はじめに
ReactiveProperty同士で双方向バインディングしたい状況があり、その方法について調査したのですが記述している情報がネット上でないっぽいので紹介がてら投稿します。
状況
こんな感じでViewModelではプリミティブっぽい型で入力を受けて、正しい情報はModelで保持。その際にはDDD的なValueObjectに変換済みにしておきたい状況です。
ちなみにModelがINotifyPropertyChangedを継承している場合のToReactivePropertyAsSynchronizedを使用した双方向バインディングの例は多数ありました。
Inputに入力するべき初期値がViewModelのコンストラクタ時点で入手可能ならばViewModel -> ModelのOneWay的なバインディングでも良かったんですが、今回の状況としては初期値は非同期処理の後で手に入るため、Model -> ViewModelのOneWayToSource的なバインディングもしたかったです。
サンプルとライブラリ
-
- ReactiveProperty: 5.3.2
- prism: 7.1.0.431
実装例
実際に動作するソースはReactivePropertySample(自作)を参照。
public class Model
{
public ReactivePropertySlim<ValueObject> VO { get; } = new ReactivePropertySlim<ValueObject>();
public async void 非同期処理Async()
{
var ret = await なんか非同期処理();
VO.Value = new ValueObject(ret.非同期処理で手に入れた初期値);
}
}
public class ViewModel : INotifyPropertyChanged, INavigationAware
{
public event PropertyChangedEventHandler PropertyChanged;
private CompositeDisposable DisposeCollection = new CompositeDisposable();
public ReactiveProperty<string> Input { get; }
public Model Model { get; }
public ViewModel(Model _model)
{
Model = _model;
Input =
Model.VO.ToReactivePropertyAsSynchronized(
x => x.Value, // propertySelector.
x => x.Name/* ValueObject型のstirngプロパティ */, // convert.
x => new ValueObject(x), // convertBack.
ReactivePropertyMode.Default | ReactivePropertyMode.IgnoreInitialValidationError, // mode.
true // ignoreValidationErrorValue.
).SetValidateNotifyError(x => String.IsNullOrEmpty(x) ? "何か入力してください。" : null) // バリデーション.
.AddTo(DisposeCollection);
}
public void OnNavigatedTo(NavigationContext navigationContext) => Model.非同期処理Async();
}
解説
結局ToReactivePropertyAsSynchronizedを使用して実現するんですけど。
言いたいことはToReactivePropertyAsSynchronizedはINotifyPropertyChangedを型パラメーターの制約に指定しているのでINotifyPropertyChangedを継承しているReactiveProperty(Slim)でも使用できますよってことです。
まとめ
メソッドの使い方の一例として誰かの参考になればと思います。