色々なGUIプログラムを作っていると、「あるコントロールがフォーカスされた時、他のコントロールにフォーカスを移す」という処理が必要になったりします。
必要になるんです。なるということにしてください。
最初にフォーカスを受け取るコントロールを ctrlFirst
、フォーカス移動先のコントロールを ctrlTarget
とすると、 ctrlFirst
の Enter
イベントで次のように書けばいい気がします。
単純にフォーカスが移ればいいだけの場合、大抵はこれでうまくいきます。
しかし、 ctrlFirst
と ctrlTarget
が別のコントロールグループ(Panel
等)に属しており、なおかつ Label
のニーモニックキー(Alt+A 等の入力でフォーカス移動する機能)を使った場合にうまくフォーカス移動してくれません。(他にもうまくフォーカス移動しないケースがあるかもしれません)
また、 Leave
イベントが複数回呼ばれていたり、 Tab でフォーカス移動すると Validating
イベントが呼ばれず入力検証を素通りしてしまったりと、色々おかしな挙動が発生します。
Enter
イベントが呼び出されるのはまさにフォーカス移動を処理している最中であり、そこに割り込みでフォーカス移動しようとしているためにおかしくなっているのではないかと推測されます。(明確な根拠無し)
結論: Enter
イベント内で直接他のコントロールにフォーカスを移してはいけません。
…じゃあどうすればいいのか?
Enter
イベント内で直接処理しないようにすればいい、ってことですね。
コントロールの Control.BeginInvoke
メソッドを使って、後から実行してもらうようにお願いしましょう。
Control.BeginInvoke
メソッドは Win32 API でいうところの PostMessage
関数みたいなものです。*1
これを同一スレッドから呼び出すことでコントロールに対して遅延実行的なことが行えます。
Control.BeginInvoke
メソッドといえば「別スレッドからのUI制御で使われるもの」というイメージですが、同一スレッド内で活用できることもあるんだよ、というお話でした。