1

私は Windows フォーム コントロールの操作に多くの時間を費やしていますが、バックグラウンド ワーカー スレッドから使​​用しています。ユーザーがボタンをクリックしたときにフォームがロックされたくないので、これは本当に良い習慣だと思います。正直なところ、バックグラウンド ワーカー スレッドで通常実行する GUI 関連のほぼすべてのアクションで、インターフェイスはユーザーへの応答性に優れています (もっと多くの人がそうしてくれたらいいのに!)。

だから私の質問は...コントロールとやり取りする必要があるたびに、次のようなもので「呼び出す」必要があります。

if (control.InvokeRequired)
{
    //
}

標準的な練習ですよね?ただし、これにより、非常に厄介なコードが発生します。これは、私が持っているほぼすべてのコントロール タイプで、MethodInvoker デリゲートまたは何かが必要になるためです。何千行ものコードをプロテクトに追加しており、非常に時間がかかります。

現在、次のような何百もの「プロパティ設定」メソッドがあります。

private void Safe_SetLableText(Label control, string text)
{
    if (control.InvokeRequired)
    {
        control.Invoke((MethodInvoker)delegate
        {
            control.Text = text;
        });
    }
    else
    {
        control.Text = text;
    }
}

それで、他のテクニック、これを行う方法、またはコントロールが何であるか、どのスレッドにいるかに関係なく、コントロールのプロパティを常に変更できる方法はありますか?

次のようなもの: (疑似コード)

BackgroundWorker.RunWorkerAsync();

private void thing_to_do()
{
    // We are in a background thread now

    DoSomeDatabaseWorkThatTakesALongTime();

    InvokeAnyControls();

    // Do some stuff...
    controlX.Text = "123"
    controlY.Height = 300;
    controlZ.text = ControlA.text;

    RestoreAnyControls();
}
4

3 に答える 3

3

InvokeRequired次のように、コードをデリゲートでラップできます。

public static void Invoke2<TControl>(this TControl c, Action<TControl> code) where TControl : Control {

    if( c.InvokeRequired ) c.Invoke( delegate() { code(c); } );
    else code(c);
}

次に、次のように使用します。

private void Safe_SetLableText(Label control, string text) {
    control.Invoke2( c => c.Text = text );
}

もちろん、 よりも優れた名前が必要な場合もありInvoke2ますが、アイデアが気に入ってくれることを願っています。ラムダ式の構文は C# 3.0 の機能ですが、Action<T>デリゲートは .NET 2.0 の一部であるため、VS2008 以降である限り、これは .NET Framework 2.0 に対してコンパイルされます。

于 2013-08-05T08:14:27.987 に答える
1

コミュニティに価値をもたらすと思うので、自分の質問への回答を投稿しています。

1)コードを「簡素化」したかったのですが、最も重要な発見は次のことでした。

control.InvokeRequired

本当に必要ありません...ほとんど与えられています。重要なのは、バックグラウンド (または非 UI) スレッドにいる場合、コントロールを呼び出す必要があるという事実に依存できることです。

2) 呼び出しはコントロール ツリーを「上へ」移動するため、次の場合:

フォーム > コントロール > コントロール内のコントロール > etc > etc

"Form" (最上位) を呼び出すだけで、子要素のプロパティを変更できます。

そこで、バックグラウンド ワーカー (または非 UI スレッド) を操作するためのクリーンでシンプルなソリューションを次に示します。私は今これをテストしましたが、うまくいきます。

public partial class Form1: Form
{
    public Form1()
    {
        BackgroundWorker bgw = new BackgroundWorker();
        bgw.DoWork += new DoWorkEventHandler(this.bgDoWork);
        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.bgComplete);
        bgw.RunWorkerAsync();
    }

    private void bgComplete(object sender, EventArgs e)
    {
        // You are not in the UI thread now, so you can Invoke without error
        this.Invoke((MethodInvoker)delegate
        {
            // Now you can change any property an any control within this form.
            // Remember "this" refers to Form1.
            this.label1.Text = "test123";
            this.label2.Text = "test456";
            this.label3.Text = this.label4.Text;

            // You can set progress bars too, not just label text
        }
    }

    private void bgDoWork(object sender, DoWorkEventArgs e)
    {
        // Do something that takes a long time
    }
}
于 2013-08-06T08:38:56.813 に答える
0

バックグラウンド ワーカーを既に使用しているので、OnProgressChangedを「誤用」しないのはなぜですか?

private void thing_to_do()
{
    // We are in a background thread now
    DoSomeDatabaseWorkThatTakesALongTime();

    BackgroundWorker.ReportProgress(1, "state");

    DoSomeMoreDatabaseWorkThatTakesALongTime();

    BackgroundWorker.ReportProgress(2, YourObjectHere);
}

void OnProgressChanged(ProgressChangedEventArgs progressArgs)
{
   switch(progressArgs.ProgressPercentage)
   {
       case 1:
          // Do some stuff...
          controlX.Text = "123"
          controlY.Height = 300;
          controlZ.text = ControlA.text;
       break;
       case 2:
          // other stuff
          YourObject obj = (YourObject) progressArgs.UserState;
          // wahtever...
       break;
       default:
       break;
   }

}
于 2013-08-05T08:41:31.950 に答える