C# BackgroundWorker实现WinForm异步操作的例子
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
在最近的一个Smart Client项目中,为了演示异步操作的实现,写了一个基于BackgorundWorker的例子。
由于这个理基本上实现了BackgorundWorker的大部分功能:异步操作的启动、操作结束后的回调、异步操作的撤销和进度报告等等。
尽管没有太多的技术含量,姑且放上来与大家分享。
1、场景描述
下面是程序运行时的截图。
本程序模拟这样的一个场景:有两组相互独立的数据需要逐条获取和显示,左边和右边两个groupbox分别代表基于这两组数据的操作,由于他们完全独立,因此可以并行执行。
当点击Start按钮,以异步的方式从存储介质中逐条获取数据,并将获取的数据追加到对应的ListBox中,ProgressBar真实反映以获取的数据条数和总记录条数的百分比,同时,当前获取的条数也会在下方的Label上随着操作的继续而动态变化。
此外通过点击Stop按钮,可以中止掉当前的操作。
当操作被中止后,ProgressBar和Label反映中止的那一刻的状态。
2、代码实现
由于界面上左右两边是两个互不干扰、相互独立的操作,所以分别创建了两个BackgroundWorker组件来负责(如下图:backgroundWorkerLeft和backgroundWorkerRight)。
将两个BackgroundWorker的WorkerReportsProgress和WorkerSupportsCancellation设为true。
我们假设获取的记录数固定,我们为此定义一个常量:
private static int MaxRecords = 100;
下面是左边Start按钮的Click event handler:
private void buttonStartLeft_Click(object sender, EventArgs e)
{
if (this.backgroundWorkerLeft.IsBusy)
{
return;
}
this.listBoxLeft.Items.Clear();
this.backgroundWorkerLeft.RunWorkerAsync(MaxRecords);
this.buttonStartLeft.Enabled = false;
this.buttonCacnelLeft.Enabled = true;
}
当Start按钮被点击后,RunWorkerAsync方法被掉调用,我们定义的常量(MaxRecords )当作参数被掺入。
随后,将会触发其DoWork事件,Dowork event handler处理代码如下:
private void backgroundWorkerLeft_DoWork(object sender, DoWorkEventArgs e)
{
try
{
e.Result = this.RetrieveData(this.backgroundWorkerLeft, e);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
throw;
}
}
调用RetrieveData方法逐条获取数据。
注意该方法的两个参数:BackgroundWorker和DoWorkEventArgs 对象,返回值是返回数据的数量。
由于在buttonStartLeft_Click中,我们将常量MaxRecords 作为参数传入了BackgroundWorker的RunWorkerAsync方法,此时的e.Argument = MaxRecords。
之所以要将这两个参数传入RetrieveData()方法,是因为该方法是为两个BackgroundWorker服务的,需要通过参数来区别当前是哪个BackgroundWorker。
我们再来看看RetrieveData方法的定义:
private int RetrieveData(BackgroundWorker worker, DoWorkEventArgs e)
{
int maxRecords = (int)e.Argument;
int percent = 0;
for (int i = 1; i <= maxRecords; i++)
{
if (worker.CancellationPending)
{
return i;
}
percent = (int)(((double)i / (double)maxRecords) * 100);
worker.ReportProgress(percent, new KeyValuePair<int,string>(i,Guid.NewGuid().ToString())); Thread.Sleep(100);
}
return maxRecords;
}
通过 e.Argument,获得最大数据获取量之后,进行一个for循环,在每次迭代中,如何worker.CancellationPending==true,代表异步操作被显示取消,则直接返回;否则,调用BackgroundWorker的ReportProgress方法。
ReportProgress具有两个重载:
•public void ReportProgress(int percentProgress);
•public void ReportProgress(int percentProgress, object userState);
percentProgress代表当前进度,从0-100。
userState便于传入一些额外的参数。
在界面上,由于数据的当前数量需要实时地显示,而记录也是现取现加(取出一条就在ListBox上追加)。
所以制定一个KeyValuePair<int,string>对象作为第二个参数。
其中Key为当前记录数,Value 是一个Guid,代表取出的数据。
ReportProgress的调用将会导致ProgressChanged事件被触发。
ProgressChanged event handler 用于显示当前进度、当前记录数量和显示获取的纪录:
private void backgroundWorkerLeft_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
KeyValuePair<int,string> record = (KeyValuePair<int,string>) erState ;
belResultLeft.Text = string.Format("There are {0} records retrieved!", record.Key);
this.progressBarLeft.Value = e.ProgressPercentage;
this.listBoxLeft.Items.Add(record.Value);
}
注:这些操作需要操作UI上的控件,只能在Main Thread中进行。
如何在RetrieveData方法进行的话,由于该方式是一个异步方法,是会抛出异常的。
由于操作的时间可能无法预知,在长时间不能完全获取数据的情况下,用户可以需要手工结束掉当前的操作。
这个操作实现在Stop按钮的Click事件中:
private void buttonCacnelLeft_Click(object sender, EventArgs e)
{
this.backgroundWorkerLeft.CancelAsync();
}
如何操作正常地结束,BackgroundWorker的RunWorkerCompleted会被触发,下面是RunWorkerCompleted
event handler的定义:
private void backgroundWorkerLeft_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
try
{
belResultLeft.Text = string.Format("Total records: {0}", e.Result);
this.buttonStartLeft.Enabled = true;
this.buttonCacnelLeft.Enabled = false;
}
catch (TargetInvocationException ex)
{
MessageBox.Show(ex.InnerException.GetType().ToString());
}
}
上面介绍的是界面左边功能的实现,右边部分的实现完全一致。
干兴趣的朋友可以参考Source Code.
本文来自CSDN博客,转载请标明出处:/bydxyj/archive/2009/03/04/3955828.aspx。