【问题标题】:BackgroundWorker ProgressChanged in a form being triggered from a separate classBackgroundWorker ProgressChanged 在从单独的类触发的表单中
【发布时间】:2012-10-21 03:56:26
【问题描述】:

我是 C# 的新手,我正试图围绕面向对象编程展开思考。这是我想要完成的任务:

我有一个带有 Form1 的 Windows 窗体应用程序和一个名为 connect 的单独类(相同的命名空间)。

我了解在执行大量代码时使用后台工作程序是一种很好的做法(以免冻结 UI)。所以我在 Form1 中创建了一个后台工作者和一个 progresschanged 句柄。我试图找到如何在单独的连接类中触发progresschange。在 Form1 的 progresschange 块中,我有一个大小写/开关,用于确定屏幕上显示的文本。

解决此问题的最佳方法是什么?我应该将后台工作人员传给其他班级吗?

【问题讨论】:

  • 为什么要在连接类上处理progress_changed?
  • connect 类运​​行了几件事,比如连接到 sql server,运行一两个查询。我希望最终能够更改 form1 上的 statusstrip 标签以反映当前正在发生的事情(例如显示连接到服务器、运行查询等)。如果我可以使用另一种不会锁定 UI 的方法从 connect 类更新 form1 上的状态条标签,我完全赞成。

标签: c# class backgroundworker


【解决方案1】:

您也可以将 BackgroundWorker 放在 Connect 类中。然后,在此类中,您可以创建事件以将进度和完成情况报告回调用表单。

using System;
using System.ComponentModel;

namespace WindowsTest
{
public class Connect
{
    BackgroundWorker bw;
    public Connect()
    {
        bw = new BackgroundWorker();
        bw.WorkerSupportsCancellation = true;
        bw.WorkerReportsProgress = true;
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        bw.DoWork +=new DoWorkEventHandler(bw_DoWork);
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
    }

    public delegate void ProgressChangedHandler(Object sender, ProgressChangedEventArgs e);
    public event ProgressChangedEventHandler ProgressChanged;
    protected void RaiseProgressChangedEvent(ProgressChangedEventArgs e)
    {
        if (ProgressChanged == null)
        {
            return;
        }
        ProgressChanged(this, e);
    }

    public delegate void WorkCompleteEventHandler(Object sender, RunWorkerCompletedEventArgs e);
    public event WorkCompleteEventHandler WorkComplete;
    protected void RaiseWorkCompleteEvent(RunWorkerCompletedEventArgs e)
    {
        if (WorkComplete == null)
        {
            return;
        }
        WorkComplete(this, e);
    }

    public void Cancel()
    {
        if (bw.IsBusy)
        {
            bw.CancelAsync();            
        }
    }

    public void BeginLongRunningProcess()
    {
        bw.RunWorkerAsync();
    }

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        RaiseWorkCompleteEvent(e);
    }

    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        RaiseProgressChangedEvent(e);
    }

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        if (ConnectToServer())
        {
            bw.ReportProgress(0, "Connected to server");
            e.Result = LongRunningProcess();
            if (e.Result.ToString() == "Cancelled")
            {
                e.Cancel = true;
                return;
            }
        }
        else
        {
            //Connection failed
            e.Cancel = true;    
        }
    }

    private bool ConnectToServer()
    { 
        //Attempt connection
        return true;
    }

    private string LongRunningProcess()
    {
        int recordCount = 250;
        for (int i = 0; i <= recordCount; i++)
        {
            if (bw.CancellationPending)
            {
                return "Cancelled";
            }
            double progress = ((double)i / (double)recordCount) * 100;
            bw.ReportProgress((int)progress , "Running Process");
            System.Threading.Thread.Sleep(25);
        }
        return "The result is 2";
    }
}
}

然后您只需在表单上处理事件。

namespace WindowsTest
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    Connect connect;

    private void btnConnect_Click(object sender, EventArgs e)
    {
        connect = new Connect();
        connect.ProgressChanged += new ProgressChangedEventHandler(connect_ProgressChanged);
        connect.WorkComplete += new Connect.WorkCompleteEventHandler(connect_WorkComplete);
        connect.BeginLongRunningProcess();
    }

    private void connect_WorkComplete(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            lblStatus.ForeColor = Color.Red;
            lblStatus.Text = string.Format("Status: {0}", "User Cancelled!");
            return;
        }

        if (e.Error != null)
        { 
            //Process exception       
            lblStatus.Text = string.Format("Status: {0}", "Error!");
            lblStatus.ForeColor = Color.Red;
            return;
        }

        lblStatus.Text = string.Format("Status: Complete. {0}", e.Result);
    }

    private void connect_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
        lblStatus.Text = string.Format("Status: {0}", e.UserState.ToString());
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        if (connect != null)
        {
            connect.Cancel();
        }
    }        
}
}

请注意,BackgroundWorker.ReportProgress() 方法的第二个参数(UserState)可以传递任何对象。它不一定是字符串。例如,如果您从数据库中检索一行/多行,则可以通过此方法将该行传递回用户界面。 BackgroundWorker.DoWork 方法的 e.Result 也是如此。

埃里克

【讨论】:

  • 谢谢!我也会看看这个
【解决方案2】:

您可以将连接类的步骤分解为单独的方法并按如下顺序调用它们。您不必从连接类对象触发progresschanged事件。

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
     ConnectClass conObject= new ConnectClass();
     backgroundWorker.ReportProgress(30, "Connecting ...");
     conObject.Connect();
     backgroundWorker.ReportProgress(30, "Connected."+"\n Executing query");
     conObject.Execute();
     backgroundWorker.ReportProgress(40, "Execution completed.");
}

【讨论】:

  • ReportProgress int 参数是否每次都必须是不同的值?我看到第一个和第二个具有相同的 int - 后台工作人员是否认识到存在更改,因为它只是再次被调用?
  • int 表示已完成工作的百分比。所有任务加起来应为 100。progresschanged 事件将由 ReportProgress 调用触发。您必须捕获通过ReportProgress 在progress_changed 事件中调用并在状态栏上相应地更新消息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-08
  • 1970-01-01
  • 2020-05-30
相关资源
最近更新 更多