初心者が迷ったReactivePropertyを使用した通知サイクルの改良と再入ブロック処理
(2016/02/07)ReactiveProperty 2.5で再入ブロックが不要になったので、追記したコメントを書き換えました。
改良型の数値型のReactiveProperty+MVVMパターンでは、以下の通知サイクルが回るようになっています。
Modelに異なる値を入力した時:
M
Modelに同じ値を入力した時:
M:10m→M変更:10m→M Stop(同値)
Viewに異なる値を入力した時/VMでの再フォーマット不要:
Viewに異なる値を入力した時/VMでの再フォーマット必要:
V:"0"→V変更:"50.0"→VM:"50.0"→M:50m→VM:"50"(→V:"50")→VM Stop(同値)
V→VM
Viewに同じ値を入力した時:
V:"60"→"60"→VM Stop(同値)
Viewに異なる値を入力したがModelが変わらない時
V:"60"→V変更:"60.0"→VM:"60.0"→M:60m→VM:"60"(→V:"60")→VM Stop(同値)
(VMでの再フォーマット必要/改良により中断しなくなった)
通知サイクルは、Model、および、View Modelの値が変わらなくなった時に停止します。
ここまでは、何とかなったのですが、問題はX, Yの場合の5のパターンです。
View Modelの基本的な部分が完成したかと思いきや、少し問題があります。
- 0 <= X, Y, Z = X + Y <= UpperBound = 100
- Decimals = 0
- X = Y = 0
- X = 50を入力
- UpperBoundY = 50
- Y = 60を入力
- Yはエラー
- Yは変更したくないので、X = 20を入力
- UpperBoundY = 80
- Yのエラーは解除されない
- Yに60を入力し直しても、View Modelには"60"が入力済みなので、再評価されない
- Yに60.0を入力し直すと、再評価されてエラーが消えるが、ユーザフレンドリじゃない・・・
この現象を回避するためには、以下のコードを考えた。
とか、気持ち悪いが、実態はgetterの値をsetterに渡しているだけなので悪いとは言えないが・・・
これは、値が変わっていないので動かない。
ModelのReactivePropertyModeをRaiseLatestValueOnSubscribeのみにするのは見送ったが、View Modelでは問題は無いので、これを使うしかないか・・・
っと、思ったのは甘かった。
StackOverflowException
まあ、ループが出来て当たり前か・・・
別に、ダブルバッファにしてサンプリングしているわけじゃないし・・・
どっかで、ブロックして、setterへの再入による無限ループを切らないといけない。
再入防止処理は、簡単なブロックオブジェクトを使って、Subscribe()全体をブロックするのが良い。
ブロックオブジェクトは、XとYのように依存関係があるものは別々に定義します。
使い方は、usingステートメントを使用します。
この方法だと、無駄なConvertBackもブロックできるので、他のプロパティにも実装した改良版がこちら。
2016/02/07 修正:
かずき大先生にお願いしたところ、ReactiveProperty 2.5でModeによらず、Viewで最後に入力された値の強制再評価と再通知が行えるようになったため、再入ブロック処理が不要となりました。
アップデートお疲れ様でした。