初心者が迷ったReactivePropertyを使用した数値型のView Modelの通知サイクルの改良
前回のSubscribe()の実装の補足です。
今扱っているのは、表示や有効数字は小数点以下の桁数が決まっているけど、小数点以下の省略した0や、入力時の余計な0は良きに計らってくれるという、何とも贅沢なGUIの例です。
しかし、数値型をそのままViewにバインドした時は、固定桁数ならStringFormatで出来ちゃうので、実はそんなに贅沢ではないのですが、フォーマットした文字列をバインドしたときは、ちゃんと罠が用意されているという話です。(もちろん、数値型をそのままViewにバインドした時も別の罠があるのは言うまでもないですが・・・)
そもそも、数値型のReactiveProperty+MVVMパターンでは、以下の通知サイクルが回るようになっています。(表示や有効数字は小数点以下0桁だけど、入力時の余計な0は良きに計らってくれるGUIの例)
通知サイクルは、ModelかView Modelの値が変わらなくなった時に停止します。
最後のパターン6は、再フォーマットが必要なのに途中で止まってしまうので、入力したままの文字列が表示されるだけです。
6のパターンを、4のパターンに変更するためには、以下の追加の実装が必要でした。
3~5の場合は、Subscribe()の1行目のmodel.Decimals.Value = iを実行することで、2行目は何もせずに終了するので、パターンに変化はありません。
6の場合は、Subscribe()の1行目は何もせずに終了しますが、2行目で目論見通り4のパターンの処理を継続してくれます。
なお、他の方法ですが、以下の理由で不採用としました。
- ModelのReactivePropertyのコンストラクタでDistinctUntilChangedの指定をやめて同値チェックを無くす
- Modelの同値チェックが無くなるとView Modelでしかチェックできなくなるので通知サイクルが長くなる
- Modelを別のReactivePropertyが監視している時に副作用を起こす(今回の事例では同値でもIsDirtyが立つ)
- そもそもModelを通常のPOCOにした時はReactivePropertyの機能はあてにできない
- Modelが通常のPOCOの場合は強制通知機能を実装してModelの強制通知機能を呼び出す
- BindableBaseとか、通知メソッドはprotectedなので、いちいちpublicなメソッドの実装が必要
- Modelを別のReactivePropertyが監視している時に副作用を起こす(今回の事例では同値でもIsDirtyが立つ)
- 「普通のModelクラスでガンバレ」って時点でReactivePropertyをわざわざ使うモチベーションが半減
2016/02/05追記, 2016/02/07修正:
は、Modelの値をフォーマットし直して表示するためには必要悪とは言え、それほど珍しくもないdecimalのパターンでの実装としては、毎回変換している辺りが、イマイチいけていない・・・
model.Decimals.Value.Convert()は、ToReactiveProperty()の実態のコンストラクタを見ると、sourceに対応している筈です。
ReactiveProperty.cs
数値が同じだがフォーマットの違う文字列を入力し続けると、SetValue()でLatestValueがViewに上書きされ続けるので、sourceの最後の値を取り直すしか無いのですが、今のところ利用する手立てはありません。