別スレッドからの操作 [C#]

ThreadやBackgroundWorkerなどを使用して別スレッドで処理を行うことがありますが、通常の方法では別スレッドからFormにあるコントロールのプロパティなどを操作することは出来ません。

例えば、以下のようにBackgroundWorkerを使用するとします。

BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);

以下の処理を「bw.RunWorkerAsync()」によって実行すると実行時にエラーとなります。
void bw_DoWork(object sender, DoWorkEventArgs e)
{
    //有効でないスレッド間の操作:
    //コントロールが作成されたスレッド以外のスレッドから
    //コントロール 'progressBar1' がアクセスされました。
    progressBar1.Maximum = 100;
}
上記ではプログレスバーのMaximumプロパティを設定していますが、別スレッドから行うことはできません。
(プロパティの値を取得することは可能です。)

そこで「Invoke」メソッドを使用します。
Invokeメソッドに渡すDelegate型のメソッドはdelegateで型を定義するか、戻り値、引数が必要ないならMethodInvokerを使用するのが良いでしょう。

MethodInvoker Sample
this.Invoke((MethodInvoker)delegate()
    {
        progressBar1.Maximum = 100;
    }
);



引数を必要とする場合の例

まず、渡すメソッドの型を定義します。
delegate void SetPrgrsBarProp(int max, int step);

そして以下がInvokeメソッドに渡すメソッドになります。
SetPrgrsBarProp spbp = (max, step) =>
{
    progressBar1.Maximum = max;
    progressBar1.Step = step;
};

Invokeメソッドにはメソッドとそれに使用する引数を別々に渡します。
this.Invoke(spbp, new object[] {200, 10});



Sample Code

上記をまとめたモノですので、Maximumプロパティを2度設定しています。
while文でプログレスバーをIncrementしています。
delegate void PrgrsBarIncrement();
delegate void SetPrgrsBarProp(int max, int step);
void bw_DoWork(object sender, DoWorkEventArgs e)
{
    this.Invoke((MethodInvoker)delegate()
        {
            progressBar1.Maximum = 100;
        }
    );


    SetPrgrsBarProp spbp = (max, step) =>
    {
        progressBar1.Maximum = max;
        progressBar1.Step = step;
    };

    this.Invoke(spbp, new object[] { 200, 10 });


    PrgrsBarIncrement pi = () =>
    {
        progressBar1.Increment(1);
    };

    while (progressBar1.Value < progressBar1.Maximum)
    {
        this.Invoke(pi);
        Thread.Sleep(50);
    }
}

0 Comments:

Sony Style(ソニースタイル)
デル株式会社

Recent Posts