WinForms のフォーカスがよくわからない
こういう構造のフォームを用意する

Form
TextBox1
UserControl1
Button1
TextBox2

Form の ActiveControl プロパティに form.userControl1 をセットしてフォーカスを確認する

System.Diagnostics.Debug.WriteLine(this.userControl1.CanFocus);
// True
this.ActiveControl = this.userControl1;

System.Diagnostics.Debug.WriteLine(this.ActiveControl);
// WinFormsApp1.UserControl1
System.Diagnostics.Debug.WriteLine(this.userControl1.Focused);
// False
System.Diagnostics.Debug.WriteLine(this.userControl1.ContainsFocus);
// False
System.Diagnostics.Debug.WriteLine(this.userControl1.ActiveControl);
// null

userControl1.CanFocus が True なのでフォーカスできるはず

ActiveControl に代入後には ちゃんと中身が UserControl1 になってる
なのに userControl1 の Focused や ContainFocus は False
userControl1 の ActiveControl は null
フォーカスが行方不明
見た目上は初期位置の TextBox1 のまま

もしかして JavaScript であるようなイベントループに処理を返してから変わったり?
タイマーで 1 秒待ってから同じものを表示するようにしてみる
→全く同じ結果

ActiveControl とフォーカスは別?

だけど

this.ActiveControl = this.userControl1.button1;

にして 代入直後の時点でさっきと同じ内容を表示すると

WinFormsApp1.UserControl1
False
True
System.Windows.Forms.Button, Text: button1

ContainsFocus が True になっていて userControl1 の ActiveControl が button1 になってる
見た目上でもボタンにフォーカスがあたってる

this.ActiveControl = this.textBox2;

でもフォーカスが切り替わってカーソル位置も変わってる

UserControl だけ特殊みたい


Focus メソッドも使ってみる

this.userControl1.Focus()

WinFormsApp1.UserControl1
False
True
System.Windows.Forms.Button, Text: button1

「this.userControl1.button1」 を ActiveControl に設定したときと一緒で UserControl1 内の最初のコントロールにフォーカスがあたってる
UserControl 自体にはフォーカス当たらなそう

だけど UserControl1 の中身を空にしてから

this.userControl1.Focus()

すると

WinFormsApp1.UserControl1
True
True
(null)

フォーカスあたってるみたい
画面上ではどこにもフォーカスあたってるような見た目はなし
WinForms でウィンドウが勝手に裏側に移動する
これと関連して WinForms の地味に不便な謎挙動

◯ Form を 4 つ用意
◯ Form1 が最初に開くメインフォーム
◯ Form1 にボタンを配置してクリックすると new Form2().Show();
◯ Form2 にもボタンを配置してクリックすると new Form3().ShowDialog();
◯ Form3 は Load イベントで Form4 を開いて即閉じる

var form = new Form4();
form.Show();
form.Close();

◯ Form4 は機能なし

● 実行したらボタンを押して Form3 まで開く
● Form2 まで閉じると Form1 が見えなくなってる
● Form1 は他のウィンドウより裏側にいる (デバッグ実行中なら VS のウィンドウよりも後ろ)

Form3 では Load の代わりにボタン操作でやると再現しなかった
ウィンドウはなんでもよくて Chrome とかエクスプローラがあればその後ろに行く


この再現方法だと発生しないが これのようにウィンドウが裏側にいく場合に 裏側に行ったウィンドウをアクティブにしても IME が有効にできないケースもあった
これみたいにエクセルなどの別プログラムが原因ではなさそう
他のエクセルなどを閉じても発生してる
調べるの面倒だし 今のところはこれは調べる予定なし → 調べた
WinForms アプリで IME が効かなくなることがある (続き)
前回

発生する PC だと使ってないけど Excel を開きっぱなしで 昔から Excel 2013 以降は他のソフトの挙動が怪しくなることが時々あったので もしかして? と思って閉じてから再実行したら発生しなくなった

他も色々試すと PDF Viewer も開いてる状態では IME に影響してた
開いてるだけで別アプリの IME まで影響するのはやめてほしいなぁ
WinForms アプリで IME が効かなくなることがある
IME がおかしくなることはときどきあるけど 以前自分で作ったものでも確実に再現するのがあったので気になったから調べてみた
一見するとおかしくなりそうなところはないので 再現するのを確認しながら色々機能を消していって ライブラリ依存はなくなって WinForms だけで発生するのを確認

どういう処理にしたら発生するかを覚えれる程度まで削ったので別 PC で 1 から同じものを作ったらなぜか再現しない
見比べても違いがわからないのでフォルダごとコピーして完全に同じソースで実行しても再現しない
ビルドした exe ファイルを持ってきても何故か発生しない
環境はどっちも Windows10 1909

再現するのに不要な部分を削る過程で 序盤は削ったら再現しなくなってたのに後半は削っても再現してたとか謎な部分もあったし確実に再現するものじゃないのかも?
だけど完全なアプリの形だと他 PC でも発生してる

一応やり方

◯ Form を 3 つと UserControl を 1 つ用意
◯ Form1 が最初に開くフォーム
◯ Form1 には TextBox と Button を配置
    ◯ Button をクリックで Form2 を開く
◯ Form2 の Load イベント時に:
    ◯ UserControl1 のインスタンスを this.Controls.Add で追加
    ◯ Form3 を開いて即閉じる
◯ Form2 の FormClosing イベント時に:
    ◯ UserControl1 を this.Controls.Remove で除外
    ◯ ↑で除外した UserControl1 の Dispose メソッド呼び出し
◯ UserControl1 は押しても反応ないボタンを一つだけ配置
◯ Form3 は機能一切なし

● 実行してテキストボックス上で IME が切り替えれることを確認
● ボタンを押して Form2 を開いたら何もせずに閉じる
● テキストボックスで IME が切り替えられなくなってる

Form2 で変なことしてるのはライブラリがやってた挙動をそのまま持ってきて再現するところだけ残したから
Dispose や Remove しなかったり Form3 を開かなかったり UserControl1 のボタンを消したりすると再現しなくなる

バージョンは .NET Framework で 3.5 や 4 や 4.6.1 など
IME はデフォルトのでも Google IME でもどっちでも

続き