3

ワーカー コードを GUI コードから完全に別のクラスに分離しようとしていますが、進行状況の更新とファイル出力のために GUI にレポートできるようにしたいと考えています。たとえば、自分の GUI でワーカー クラスに「シリアル ポートから 10 行を読み取りますが、読み取った内容を受け取るたびに報告してください」と言わせたいとします。現在のところ、これを行う最善の方法は、GUI を 10 回ループさせ、各ループでワーカー クラスにコマンドを送信して 1 つのものを読み取り、それを返すことです。

ワーカー クラスの側ですべてのループを保持することを本当に好みます。これは、使用可能なデータについてより多くの情報が得られるためです (実際のデータ量は可変であり、ワーカー クラスは既にデータ量にアクセスできます)。これを GUI クラスに送り返してループ自体を実行したくありません。

私はbackgroundworkerを調べましたが、それは長い操作中に行われたパーセンテージのみを報告しているようで、他には何も報告していないようです. どうすればこれを達成できるか、誰かが良い考えを持っていますか?

以下は、私がやりたいことを (できれば) よりよく説明するためのプログラムのシェルです。私が必要とすることを行うためにコードをどのように編集しますか?

GUI のメイン クラス:

class Main_Class
{
    ...
    /*  Assume in the area we have instantiated these items and placed them on the form:
    *   Button  DoSomething:  A button to do something
    *   TextBox ShowInfo:  A text box to report something from the worker class     
    */

    Worker_Class timewaster = new Worker_Class();

    private void buttonDoSomething_Click(object sender, EventArgs e)
    {
        timewaster.a_lengthy_task();
    }
}

別のワーカー クラス:

class Worker_Class
{
    ...//Various Setup stuff up here

    void a_lengthy task()
    {
        int iteration = 0;

        while(iteration < 10)
        {
            Datetime saveNOW = Datetime.Now;        //lets say I report this back to the the GUI to write in that ShowInfo box
            Thread.sleep(10000);                    //To waste time and make this lengthy

            //Your code here to facilitate sending saveNOW back to the the Main_Class and display it on the ShowInfo textbox.

            iteration++
        }
    }
}
4

4 に答える 4

3

必要なのはイベントです。クラスに組み込まれているものを回避して動作させることができない場合BackgroundWorkerは、少なくとも、クラスの動作に合わせてソリューションをモデル化できます。

ワーカー クラスには、パブリック イベントIterationCompleteまたは任意の名前のイベントが必要です。EventArgsUI が必要とする反復に関連する情報を含むオブジェクト (または EventArgs を拡張するオブジェクト) を与えることができます。このイベントは、各反復が完了した後 (または UI の変更が行われるたびに) 発生させることができます。

その後、UI は、その反復に関連するすべての UI タスクのイベントをサブスクライブできます。

コードサンプル:

メインクラス:

public class MainClass
{
    Worker_Class timewaster = new Worker_Class();


    private void buttonDoSomething_Click(object sender, EventArgs e)
    {
        timewaster.IterationComplete += new EventHandler<Worker_Class.IterationEventArgs>(timewaster_IterationComplete);
        timewaster.LengthyTask();
    }

    void timewaster_IterationComplete(object sender, Worker_Class.IterationEventArgs e)
    {
        string infoFromWorker = e.iterationNumber;
    }
}

ワーカー クラス:

public class Worker_Class
{
    //Various Setup stuff up here

    public class IterationEventArgs : EventArgs
    {
        public string iterationNumber { get; set; }
    }

    public event EventHandler<IterationEventArgs> IterationComplete;

    public void LengthyTask()
    {
        int iteration = 0;

        while (iteration < 10)
        {
            DateTime saveNOW = DateTime.Now;        //lets say I report this back to the the GUI to write in that ShowInfo box
            Thread.Sleep(10000);                    //To waste time and make this lengthy

            //Your code here to facilitate sending saveNOW back to the the Main_Class and display it on the ShowInfo textbox.

            if (IterationComplete != null)
            {
                IterationEventArgs args = new IterationEventArgs();
                args.iterationNumber = iteration.ToString();
                IterationComplete(this, args);
            }

            iteration++;
        }
    }
}
于 2012-05-25T20:39:32.120 に答える
1

Worker_Classイベントを介して進行状況をブロードキャストする必要があります。はこのMain_Classイベントをサブスクライブし、それに応じてUIを更新するために情報を使用します。ここから2つの方向に分岐できます。Control.InvokeこのイベントハンドラーをUIスレッドにマーシャリングするために使用できます。UIスレッドでは、GUIを安全に更新できます。または、イベントハンドラーに進行状況情報を共有変数に保存させることもできます。次に、UIに、System.Windows.Forms.Timerまたはを介して都合のよい間隔でこの変数をポーリングさせますDispatcherTimer。この場合、私は後者の方法を好みます。

class YourForm : Form
{
  private volatile MyEventArgs progress = null;

  private void buttonDoSomething_Click(object sender, EventArgs args)
  {
    var timewaster = new Worker_Class();
    timewaster.ProgressChanged += (sender, args) { progress = args; };
    Task.Factory.StartNew(
      () =>
      {
        timewaster.a_lengthy_task();
      }
    UpdateTimer.Enabled = true;
  }

  private void UpdateTimer_Tick(object sender, EventArgs args)
  {
    if (progress != null)
    {
      if (progress.IsFinished)
      {
        ShowInfo.Text = "Finished";
        UpdateTimer.Enabled = false;
      }
      else
      {
        ShowInfo.Text = progress.SaveNow.Value.ToString();
      }
    }
    else
    {
      ShowInfo.Text = "No progress yet";
    }
  }
}

class Worker_Class
{
  public event EventHandler<MyEventArgs> ProgressChanged;

  public Worker_Class()
  {
    ProgressChanged += (sender, args) => { };
  }

  public void a_lengthy task()
  {
    int iteration = 0;
    while(iteration < 10)
    {
        Datetime saveNOW = Datetime.Now;
        Thread.sleep(10000);
        ProgressChanged(this, new MyEventArgs { SaveNow = saveNOW, IsFinished = false });
        iteration++
    }
    ProgressChanged(this, new MyEventArgs { SaveNow = null, IsFinished = true });
  }
}

class MyEventArgs : EventArgs
{
  public DateTime? SaveNow { get; set; }
  public bool IsFinished { get; set; }
}
于 2012-05-25T21:09:22.477 に答える
1

BackgroundWorkerこれはクラスにとって理想的なタスクです。コンパクトなフレームワークを使用していないと仮定すると、使用できるはずです。

でとイベントを聞くBackgroundWorkerことができます。これらは UI スレッドにマーシャリングされ、winform/wpf コントロールを安全に更新できます。進行状況を表示するには、onCompletedonProgressUpdatedReportProgress()DoWork()BackgroundWorker

コードを別のスレッドに取り込むときは、GUI コントロールを更新するときに注意する必要があります。コントロールを更新するスレッドとして、UI スレッドである必要があります。BackgroundWorker がこれを処理します。

MSDN - http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

于 2012-05-25T20:58:26.537 に答える
0

私のバックグラウンドワーカーでは、次のものを使用します。

this.Invoke((MethodInvoker)delegate
{
    //do something in GUI
});

たとえば、GUIでラベルを変更する場合は、

label.Text = "whatever";

メソッド内。

于 2012-05-25T20:40:22.143 に答える