利用BackgroundWorker进行后台操作并在另一个单独的窗体中显示操作进度稍微有些复杂,但是我们已经有了些许基础,所以继续上一篇博文然后对源码进行升级!
先看下button5_Click的代码:
private Form2 m_frmProgress = null;
private BackgroundWorker m_AsyncWorker2 = null;
private void button5_Click(object sender, EventArgs e)
{
if (m_AsyncWorker2 == null)
{
button5.Enabled = false;
m_AsyncWorker2 = new BackgroundWorker();
m_AsyncWorker2.WorkerReportsProgress = true;
m_AsyncWorker2.WorkerSupportsCancellation = true;
m_AsyncWorker2.DoWork += new DoWorkEventHandler(m_AsyncWorker2_DoWork);
m_AsyncWorker2.ProgressChanged += new ProgressChangedEventHandler(m_AsyncWorker2_ProgressChanged);
m_AsyncWorker2.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_AsyncWorker2_RunWorkerCompleted);
m_frmProgress = new Form2(m_AsyncWorker2);
m_AsyncWorker2.RunWorkerAsync();
m_frmProgress.ShowDialog(this);
}
}
和上一篇博文的button4_Click内容几乎一样,只是多了关于进度窗体Form2的操作。我们首先声明Form2类型的变量m_frmProgress,但不实例化,然后常规似的对BackgroundWorker的属性和方法进行赋值,在执行异步操作之前我们需要实例化进度窗体。对于进度窗体的构造函数我们做了改造,在创建Form2实例的同时传递BackgroundWorker,这个传递实际上是引用传递,也就是说传递的是m_AsyncWorker2的地址(.Net又将此行为隐藏了!),这样我们就可以在Form2里对BackgroundWorker进行控制,例如调用BackgroundWorker.CancelAsync()。
因为button5_Click方法内的代码是非阻塞的,所以调用完m_AsyncWorker2.RunWorkerAsync()异步方法后m_frmProgress.ShowDialog(this)得以立刻执行,进度窗体就模式化的显示出来了。
在进度窗体Form2中我们需要放置一个label、一个progressbar和一个button,创建一个BackgroundWorker类型的变量m_bw,用于接受通过Form2的构造函数传递过来的BackgroundWorker。button的单击用于取消后台操作,亦即调用BackgroundWorker.CancelAsync(),随即关闭Form2。我们的版本还允许直接单击窗体标题栏上的关闭按钮来终止后台操作并关闭窗体。Form2的源码如下:
public partial class Form2 : Form
{
private BackgroundWorker m_bw = null;
public Form2(BackgroundWorker bw)
{
InitializeComponent();
m_bw = bw;
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
if (m_bw.IsBusy)
{
m_bw.CancelAsync();
}
}
private void button1_Click(object sender, EventArgs e)
{
m_bw.CancelAsync();
this.DialogResult = DialogResult.Cancel;
}
}
BackgroundWorker.DoWork事件则与前一篇博文的内容几乎完全一致,只不过需要注意一点,因为创建窗体肯定需要时间,立刻开始耗时操作并报告进度时进度窗体也许还未创建完毕,所以我们最好等待些许毫秒。
报告进度的操作也与前篇博文内容差不多,只不过progressbar和label是在另外一个窗体上。那么通过界定m_frmProgress可以访问吗?例如:
m_frmProgress.progressBar1.Value = e.ProgressPercentage;
m_frmProgress.label1.Text = e.ProgressPercentage.ToString() + "%";
这样做编译器肯定会报错,不过没关系,我们只要在VS IDE的解决方案资源管理器中展开Form2.cs,并打开Form2.Designer.cs就会看到问题所在。微软不希望初级开发者随意对*.Designer.cs进行改动,所以无论看到什么警告我们都要做到:不要理会他!我们不是菜鸟!
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button button1;
原来窗体上的控件的访问级别都是private!那好吧,我们就把它改成public呗。
到这里我们来看看自己的BackgroundWorker实例的相关源码:
private void m_AsyncWorker2_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bwAsync = sender as BackgroundWorker;
int iCount = new Random().Next(20, 50);
Thread.Sleep(100); //等待,以便frmProgress创建完毕!!!
for (int i = 0; i < iCount; i++)
{
Thread.Sleep(100);
bwAsync.ReportProgress(Convert.ToInt32(i * (100.0 / iCount)));
if (bwAsync.CancellationPending)
{
Thread.Sleep(1200);
e.Cancel = true;
return;
}
}
bwAsync.ReportProgress(100);
}
private void m_AsyncWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
m_frmProgress.progressBar1.Value = e.ProgressPercentage;
m_frmProgress.label1.Text = e.ProgressPercentage.ToString() + "%";
}
private void m_AsyncWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (m_frmProgress != null)
{
m_frmProgress.Close();
m_frmProgress = null;
}
button5.Enabled = true;
if (e.Error != null)
{
MessageBox.Show(this, e.Error.Message);
return;
}
if (e.Cancelled) //Cancelled...
{
MessageBox.Show(this, "Operation is canceled.");
}
else //Completed...
{
MessageBox.Show(this, "Operation complete!");
}
m_AsyncWorker2 = null;
}
我们已经利用BackgroundWorker创建了一种比较专业的后台操作前台显示进度的框架,有兴趣的朋友可以对其进行丰富和扩充。