What does it mean?
MVVMの場合、コードビハインドにロジックを書くことを厭い、Behaviorを使って、UIとロジックの分離を試みることも多いでしょう。
Behaviorの場合、System.Windows.Interactivity.Behavior を継承し、型パラメータは System.Windows.DependencyObject の派生型である System.Windows.FrameworkElement を指定することがほとんどです。少なくとも私は。
そのため、任意のプロパティのコールバックイベントで、引数に渡ってくる DependencyObject の AssociatedObject プロパティから、コールバックの呼び出し元を取り出します。
すなわち、Behaviorを追加した箇所 の FrameworkElement です。
その後、System.Windows.VisualStateManager.GoToElementState メソッドで状態名を指定してUIの状態を変化させます。
ここまでは普通です。
GoToElementState does Not work!!!
ところが、GoToElementStateがfalseを返すことがあります。
例外を返してくれるか、エラーコードが返るなら良いのですが、bool値を返されても原因がわかりません。
そもそもMSDNのGoToElementStateの説明でも
true コントロールが正常に新しい状態に遷移した場合それ以外の場合、 falseです。
という有様。不親切すぎる。
VisualStateはありますか?
ここで原因ですが、GoToElementStateの第一引数は、FrameworkElementです。つまり、状態を遷移させる対象となる要素になります。
失敗していたのは、VisualStateを定義していない位置にBehaviorを定義していたからです。
以下サンプルソースです。
1 | <Window x:Class="WPF3.Views.MainView" |
1 | using System.Windows; |
BooleanVisualStateBehaviorは、依存関係プロパティであるStateをトリガーにして、TrueStateとFalseStateに指定した状態名への遷移を試みるBehaviorです。
このBehaviorがWindowとGridの直下に存在します。
これが問題で、Behaviorで渡ってくるFrameworkElementがWindowとGridになります。
つまり、WindowとGridの直下にあるVisualStateを操作しようにも、Gridの下にはVisualStateが定義されていないから何も起きないわけです。
ですので解決策は、
- 2つめのBooleanVisualStateBehaviorであるState2Changeを1つめのBooleanVisualStateBehaviorであるState1Changeと同じ位置に持ってくる。
つまり
1 | <i:Interaction.Behaviors> |
とします。
または、
- 2つめのVisualStateGroupであるState2をGridの下に移動する
になります。
Conclusion
原因としては、非常に陳腐ですが、メソッドが動かない理由がわかりづらいため、解決に少し時間を取られました。
VisualStateは便利ですが、XAMLでの定義が間違ったりすると例外を投げるくせに、遷移で失敗しても例外を投げないなど、統一感が無くて困ります。
まぁ、一番悪いのは自分ですがね。何故、Behaviorをルール要素では無く、ネストした要素の下に書いたんですかねぇ。