別スレッドからフォームコントロールを操作するには?

VB.NET にも該当することですが、C#.NET では、

using System;
using System.Windows.Forms;
using System.Threading;
 
namespace Foo{
public class Form1 : Form{
    public Form1(){
        InitializeComponent();
    }
 
    private void button1_Click(object sender, EventArgs e){
        new Thread(new ThreadStart(delegate{
textBox1.Text = “Hello, world”;
})).Start();
    }

    // その他については略
}
}

としても InvalidOperationException が発生し、

有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール ‘textBox1’ がアクセスされました。

のように表示されてしまい、クロススレッドなフォームコントロールの操作ができません。
 
これを回避するには、System.Windows.Forms.Control の Invoke() メソッドを使い、コントロールの操作時のみ、そのコントロールが属するウィンドウハンドルを持つスレッドで実行するようにします。

            // Invoke() 用デリゲート
            private delegate void Del();
 
            private void button1_Click(object sender, EventArgs e){
 
                new Thread(new ThreadStart( delegate {
Invoke( (Del) delegate { // 匿名メソッドを Del 型にキャストし, それを呼び出す。
textBox1.Text = “Hello, world”; // コントロールの操作
});
                    })).Start();
            }

あるいは、MSDN の「Windows フォーム コントロールのスレッド セーフな呼び出しを行う」にあるように、

            private void button1_Click(object sender, EventArgs e){

new Thread(new ThreadStart( delegate {
SetText(“Hello, world”);
})).Start();
}

private delegate void SetTextCallback(string msg);

private void SetText(string msg){
if(textBox1.InvokeRequired){ // 指定したコントロールの操作に Invoke()が必要かどうか(この場合、this.InvokeRequired としてもよい)。

Invoke(new SetTextCallback(SetText), new object[] {msg}); // 必要であれば自身を Invoke する。
return;

}

// 以下本来の処理

textBox1.Text = msg;
}

のように、Control クラスの InvokeRequred プロパティで Invoke が必要かどうかを判別し、必要なら Invoke によりメソッドを呼び直すという方法もあります。
 
どちらを利用する場合も、Invoke() 中はメインスレッドでの実行になるため、マルチスレッド処理の恩恵を受けられません。このため、Invoke() 中のコードでは、コントロールの操作をするために最低限の実装であることが望ましいでしょう。
 
なお、本記事のコードで使用している匿名メソッド( delegate (){ /* */ } というコード)は、.NET Framework 2.0 以降でしか利用できないため、2.0 未満で利用する場合は該当箇所をメソッド定義に置き換えてください。
 

追記: C# 3.0(Visual Studio 2008)以降はラムダ式が使えるようになってさらに書きやすくなりました。

Invoke( (MethodInvoker)(()=> // ラムダ式をUIスレッドで実行する。
textBox1.Text = "Hello, world";        // コントロールの操作
});
}

参考:
System.Windows.Forms.Control.Invoke()
Windows フォーム コントロールのスレッド セーフな呼び出しを行う(MSDN)
  MSDN ライブラリ -> 開発ツールと言語 -> Visual Studio ドキュメント -> Windows ベースのアプリケーション、コンポーネント、サービス -> Windows ベースのアプリケーションの作成 -> Windows フォーム -> Windows フォームについて -> Windows フォームコントロール -> .NET Framework を使用したカスタム Windows フォームコントロールの開発 -> Windows フォームコントロールのマルチスレッド処理

コメントを残す

メールアドレスが公開されることはありません。