こんな例外がでて困ってる!
有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'textBox1' がアクセスされました。'
フォームに配置したTextBoxなどのコントロールを更新しようとして、上記のような例外が発生する場合があります。
これを解決します。
有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール がアクセスされました。【原因】
【原因】別スレッドからコントロールへアクセスしたため
Windowsフォームのコントロールは、必ずコントロールを生成したスレッドから行う必要があります。
※コントロールを生成したスレッド:メインスレッド
上記の例外が発生している方は、スレッドを作成してそのスレッドからコントロールへアクセスしていませんか?
それが、例外発生の原因です。
対策は簡単です。
以下にコードを示します。
Invokeメソッドを使ってコントロールへアクセスする
動作確認用画面を作成
動作確認用の画面はこんな感じ(単純です)。
ボタンをクリックすると、その下のTextBoxに「Update」と表示するだけのもの。
ソースコード例
ソースコードは以下の通り。
ダメな例(例外が発生する)、良い例を一緒に記載しています。
※「bool error = true;」を true or false に変えると、それぞれ動作確認できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /// <summary> /// ButtonのClickイベント。 /// </summary> /// <param name="sender">イベントソース。</param> /// <param name="e">イベントデータ。</param> private void Button_Click(object sender, EventArgs e) { Task.Factory.StartNew( () => { bool error = true; if (error) { // 【誤】別スレッドからTextBoxを更新してみる→エラー発生 this.textBox1.Text = "Update"; } else { // 【正】Invokeメソッドを使用 if (this.InvokeRequired) { this.Invoke(new Action(this.UpdateText)); } else { this.textBox1.Text = "Update"; } } }); } /// <summary> /// テキストを更新する。 /// </summary> /// <param name="text">表示するテキスト。</param> private void UpdateText() { this.textBox1.Text = "Update"; } |
ダメな例:例外発生「有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール がアクセスされました。」
「bool error = true;」にして実行してみます。
ボタンをクリックすると、このように例外が発生します。
この例では、「Task.Factory.StartNew」で別スレッドを生成して、そこからコントロールにアクセスしようとしているためです。
良い例:Invokeメソッドを使用する
この部分です。
1 2 3 4 5 6 7 8 | if (this.InvokeRequired) { this.Invoke(new Action(this.UpdateText)); } else { this.textBox1.Text = "Update"; } |
this.Invoke(new Action(this.UpdateText));
これで、メインスレッドからコントロールにアクセスできるようになります。
簡単ですね。
Invokeが必要か確認する方法
この例では、Invokeメソッド呼ぶ前にこんなif文を実行しています。
if (this.InvokeRequired)
これは、Invokeが必要なのか確認するメソッドです。
必要な場合(別スレッドからアクセスしようとしている)は、「InvokeRequired」がtrueで返ります。
不要な場合(メインスレッドでアクセスしようとしている)は、「InvokeRequired」がfalseで返ります。
まとめ
- 別スレッドからのコントロールへのアクセスはNG
- Invokeメソッドを使って解決
- InvokeRequiredでInvokeメソッドの要否を確認