2

我正在运行一个 BackgroundWorker 线程来执行一项耗时的任务。耗时的任务在另一个班级。我需要将在 BackgroundWorker 上运行的这个单独的类上取得的进展传回 Main Form1 类。我不知道如何解决这个问题。请提供建议。先感谢您。

    **// Main Form1 UI Class**    

    public void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
    {
        //e.Argument always contains whatever was sent to the background worker
        // in RunWorkerAsync. We can simply cast it to its original type.
        DataSet ds = e.Argument as DataSet;
        this.createje.ProcessData(this.ds);
    }

    private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.progressBar1.Minimum = 0;
        this.progressBar1.Maximum = CreateJE.max;
        this.progressBar1.Value = e.Recno;
    }

    **//Other Class called CreateJE**

    public void ProcessData(DataSet ds)
    {
        //Do time consuming task...
     for (int i = 1; i <= 10; i++)
        {
            if (worker.CancellationPending == true)
            {
                e.Cancel = true;
                break;
            }
            else
            {
                // Perform a time consuming operation and report progress.
                System.Threading.Thread.Sleep(500);

                **//How do I report progress back to the Main UI?** 
                //worker.ReportProgress(i * 10);
            }
        }
    }
4

2 回答 2

4

最干净、最可扩展的解决方案可能是让您的ProcessData()方法引发一个事件,该事件BackgroundWorker正在侦听。这种方式ProcessData()不依赖于BackgroundWorker作为调用者。(您还需要设法取消ProcessData())。如果需要,您甚至可以重复使用ProgressChangedEventArgs。例如(未经测试,但你明白了吗?):

partial class Form1 { 
    public void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) {
        //e.Argument always contains whatever was sent to the background worker 
        // in RunWorkerAsync. We can simply cast it to its original type.
        DataSet ds = (DataSet)e.Argument;
        var bgw = (BackgroundWorker)sender;

        var eh = new ProgressChangedEventHandler((o,a) => bgw.ReportProgress(a.ProgressPercentage));
        createje.ProgressChanged += eh;
        this.createje.ProcessData(this.ds));
        createje.ProgressChanged -= eh; //necessary to stop listening
    }

    private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.progressBar1.Minimum = 0;
        this.progressBar1.Maximum = CreateJE.max;
        this.progressBar1.Value = e.ProgressPercentage;
    }

}

partial class CreateJE { 

    public event ProgressChangedEventHandler ProgressChanged; 
    protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
    { 
        var hand = ProgressChanged; 
        if(hand != null) hand(this, e);
    }

    public void ProcessData(DataSet ds)
    {
        for(int i = 1; i <= 10; i++)
        {
            // Perform a time consuming operation and report progress.
            System.Threading.Thread.Sleep(500);

            var e = new ProgressChangedEventArgs(i * 10, null);
        }
    }

}

快速而肮脏的方法是将BackgroundWorker作为参数传递给ProcessData(). 恕我直言,这是相当丑陋的,但是,将您限制为仅使用 BackgroundWorkers,并且还迫使您BackgroundWorker在一个地方(主窗体类)定义 ReportProgress 的返回值在另一个地方(CreateJE 类)。

您还可以使用计时器并每隔 X 毫秒报告一次进度,查询 CreateJE 对象的进度。这似乎与您的其余代码一致。与此有关的问题是它会使您的 CreateJE 类对多线程不友好。

于 2012-08-13T15:01:31.277 回答
1

最快和最简单的选择是delegate在类CreateJE中声明一个报告进度,然后将其挂接ReportProgressBackgroundWorker.

class CreateJE
{
    public Action<int> ReportProgressDelegate{get;set;}

    public void ProcessData(DataSet ds)
    {
        for(int i = 0; i < 10; i++)
        {
            Thread.Sleep(500);
            ReportProgress(i*10);
        }
    }

    private void ReportProgress(int percent)
    {
        if(ReportProgressDelegate != null)
            ReportProgressDelegate(percent);
    }
}

在您的表单中,初始化ReportProgressDelegate您的实例的属性(我假设this.createje是指表单的一个字段,因此OnLoad似乎是进行初始化的好地方):

protected override void OnLoad(EventArgs e)
{
    this.creatje.ReportProgressDelegate = worker.ReportProgress;
}

之后,您可以使用已有的事件处理程序(backgroundWorker2_DoWorkbackgroundWorker2_DoWork)。

PS:您应该对属性使用相同的方法worker.CancellationPending

于 2012-08-13T15:25:44.340 回答