MVVMでコレクション内のコントロールにフォーカスを当てるときにつまづいた件


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がメッセージを受け取れてないっぽい)。

処理の流れとして
1. コレクションに要素が追加される
2. 追加した要素のViewModelからメッセージを投げる
3. View上のコントロールが追加される
となってしまっている気がする。ので、なんとかして2と3の処理の順番を入れ替えたい。
悩んで導き出した解決方法が、Dispatcherを利用して非同期にフォーカスを当てる方法。

DispatcherHelper.UIDispatcher.BeginInvoke(new Action(() => Messenger.Raise(new InteractionMessage("FocusToName"))), DispatcherPriority.Background);

DispatcherPriorityをBackgroundに指定することで、要素追加のバインド処理が完了してからメッセージを投げることができるようになった。

※検証用のソースはここのFocusタブにおいてます