3

我试图将我的工作代码与我的 GUI 代码分离到一个完全不同的类中,但我希望能够向我的 GUI 报告进度更新和文件输出。例如,我想让我的 GUI 对工人类说,“从串行端口读取十行,但在收到时向我报告你读到的每件事”。目前我最好的方法是让 GUI 循环十次,并在每个循环中向工人类发送一个命令来读取一件事并返回它。

我真的更希望将所有循环都保留在我的工人阶级一侧,因为它将有更多关于可用信息的信息(实际数据量将是可变的,并且工人阶级已经可以访问数据量可用,我不希望将其发送回 GUI 类来运行循环本身。

我已经调查了后台工作人员,但这似乎只报告了在长时间操作期间完成的百分比,没有别的,所以这对我没有多大帮助。有人知道我如何做到这一点吗?

下面是一个程序的外壳,试图(希望)更好地说明我想要做什么。您将如何编辑代码以执行我的要求?

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或任何你想称之为的事件。您可以为其提供一个EventArgs对象(或扩展 EventArgs 的对象),其中包含 UI 所需的与该迭代相关的信息。该事件可以在每次迭代完成后触发(或者当 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

这是BackgroundWorker班级的理想任务。假设您不使用紧凑框架,它应该可供您使用。

BackgroundWorker您可以收听onCompletedonProgressUpdated事件。这些已为您编组到 UI 线程中,您可以使用它们安全地更新您的 winform/wpf 控件。要显示进度,请ReportProgress()DoWork()BackgroundWorker

将代码放入单独的线程时,更新 GUI 控件时需要小心。作为更新控件的线程,必须是UI Thread。BackgroundWorker 为您处理这个问题。

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

于 2012-05-25T20:58:26.537 回答
1

您将需要Worker_Class通过事件让您的广播进度。将Main_Class订阅此事件并使用该信息相应地更新 UI。您可以从这里分支到两个方向。您可以使用Control.Invoke将此事件处理程序编组到 UI 线程上,您可以在其中安全地更新 GUI。或者您可以让事件处理程序将进度信息保存到共享变量中。然后让您的 UI 在方便的时间间隔通过 aSystem.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 回答
0

在我的后台工作人员中,我使用:

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

例如,如果您想更改 GUI 中的标签,请使用

label.Text = "whatever";

方法里面。

于 2012-05-25T20:40:22.143 回答