Livetを利用している場合、ViewModelからメッセージを投げることで任意のコントロールにフォーカスを当てることができる。例えば、ViewModelからMessenger.Raise(new InteractionMessage("FocusToTextBox"));
という形でメッセージを投げて、Viewでは下記のように受け取る
<TextBox>
<i:Interaction.Triggers>
<l:InteractionMessageTrigger Messenger="{Binding Messenger}" MessageKey="FocusToTextBox" >
<l:SetFocusAction />
</l:InteractionMessageTrigger>
</i:Interaction.Triggers>
</TextBox>
ただ、この方法ではコレクションの任意のコントロールに対してフォーカスを当てたいときにうまくいかないパターンがあった。例えば、何らかのコレクションを表示していて、あるボタンを押下したときに「要素を末尾に追加+追加した要素内のコントロールにフォーカスを当てる」という動きを実現したいとき、上記の方法ではフォーカスを当てることができない(そもそもInteractionMessageTriggerがメッセージを受け取れてないっぽい)。
処理の流れとして
- コレクションに要素が追加される
- 追加した要素のViewModelからメッセージを投げる
- View上のコントロールが追加される
となってしまっている気がする。ので、なんとかして2と3の処理の順番を入れ替えたい。
悩んで導き出した解決方法が、Dispatcherを利用して非同期にフォーカスを当てる方法。
DispatcherHelper.UIDispatcher.BeginInvoke(new Action(() => Messenger.Raise(new InteractionMessage("FocusToName"))), DispatcherPriority.Background);
DispatcherPriorityをBackgroundに指定することで、要素追加のバインド処理が完了してからメッセージを投げることができるようになった。
※検証用のソースはここのFocusタブにおいてます