2007年8月8日星期三

在.NET 2.0应用程序中使用BackgroundWorker组件

年7月3日

BackgroundWorker可以让窗体异步地完成一个操作。在我们需要执行诸如『数据库事务』或者『图片下载』之类的操作时,这个功能非常有 用。此时,我们的可以让用户界面停止响应(或者隐藏起来直到操作结束)。在这篇文章中,我会一步一步教会你如何在.NET 2.0程序中使用BackgroundWorker组件以便处理较耗时的操作。示例程序使用C#编写。

与往常一样,我们创建一个测试工程,取名为"TestBGW",使之只包含一个窗体("FormBGW"):



图1.

我们将使用BackgroundWorker完成一些数据库的事务操作(比如,获取一些DataTable)。首先拖一个BackgroundWorker组件到我们窗体上。

图2.

我们将用DataTable来设置DataGridView1的DataSource属性。我们还应该刷新我们的用户界面,并告诉用户:所有的操作已经全部“OK”,他/她不用再操心啦。因此,我们还要需要一个StatusStrip和一个Timer.


图3.

为了让用户看到我们的处理过程正在运行之中,我们将用到toolStripProgressBar1:

图4.

用toolStripStatusLabel1和toolStripStatusLabelTime是来向用户显示处理过程的状态和已经花费的时间。

我们窗体看上去是这样子的:


图5.

为了模拟数据库的事务操作,我们将用到 GetData.dll(当然你也可以连接到一个真实的数据库;这个模拟的目的只是为了测试而已)。出于这个目的,我们把GetData.dll添加到引用中,然后写因getDataTable方法:

private DataTable getDataTable(int Rows)

{

GetData.GetDataHelp getData = new GetData.GetDataHelp();

return (getData.getDataSetCities(Rows).Tables[0]);

}


我们调用RunWokerAsync方法开始我们的异步操作:


private void FormBGW_Activated(object sender, EventArgs e)

{

backgroundWorker1.RunWorkerAsync();

}


BackGroundWorker组件有三个事件:

图6.


DoWork事件发生在RunWokerAsync方法被调用时。在这个事件的处理函数中,我们“完成”那些的耗时的工作(我们载入至少100000行数据以便使我们的处理过程变得“耗时”),并通知用户载入工作正在进行中,最后将我们的DataTable置为我们的异步操作的结果。


private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

DataTable dt;

toolStripStatusLabel1.Text = "Loading ... " + "Thanks for your patience";

dt = getDataTable(1000000);

e.Result = dt;

}


就我们面对的情况而言(我是指真实的情况,即从数据库获得一些DataTable的情况),我们没办法“插入”处理过程以“追踪”已获取的数据行数量(一行接一行)。当然,我们同样也没有办法知道,根据我们的请求最终可以从数据库获得多少数据行。所以呢,我们不会用DoWorkEventArgs(译注:应为BackgroudWorker的笔误)的ReportProgress 方法来触发ProgressChanged事件。我们将用Timer1,向用户显示处理过程正在进行中(我们将把 toolStripProgressBar1“增加”到最大程度,然后再从最短长度继续开始;就是说,我们会制造一些循环)。因此我们给 timer1_Tick加上下面的代码:

if (toolStripProgressBar1.Value == toolStripProgressBar1.Maximum)

{

toolStripProgressBar1.Value = 0;

}


为了让用户知道加载已经花费了多长时间,我们还得给这个方法加上一点代码,比如这个样子:

string sTime =" ..." + ts.Minutes.ToString("00") +

":" + ts.Seconds.ToString("00") +

":" + ts.Milliseconds.ToString("000");

toolStripStatusLabelTime.Text = sTime;


其中ts指现在的时间(参见下面完整的代码)。

顺便说一句,如果仅仅为了测试ReportProgress如何工作,你可以给backgroundWorker1_DoWork方法加上下面的代码:

//-------to try percentage ...

int iMax = 100000;

for (int i = 0; i < iMax; i++)

{

backgroundWorker1.ReportProgress((i * 100) / (iMax - 1));

}


然后把这些代码加入到backgroundWorker1_ProgressChanged :

private void backgroundWorker1_ProgressChanged(object sender,ProgressChangedEventArgs e)

{

//-------to try percentage ...

toolStripProgressBar1.Value = e.ProgressPercentage;

toolStripStatusLabel1.Text = "Loading ... " +

e.ProgressPercentage.ToString() + "%";

//-------------------------

}


RunWorkerCompleted事件发生在后台操作完成的时候。此时我们将“关闭”所有的关于加载的提示信息,并且把通过使用事件的参数RunWorkerComletedEventArgs,把dataGridViewCites绑定到dataTable:

dataGridViewCities.DataSource = e.Result;

FormBGW.cs的全部代码如下:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

namespace TestBGW

{

public partial class FormBGW : Form

{

public FormBGW()

{

InitializeComponent();

////to try percentage ...

//backgroundWorker1.WorkerReportsProgress = true;

}

#region "forClass"

DateTime startDate = DateTime.Now;

#endregion

private void FormBGW_Activated(object sender, EventArgs e)

{

backgroundWorker1.RunWorkerAsync();

timer1.Start();

}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

DataTable dt;

toolStripStatusLabel1.Text = "Loading ... " +

"Thanks for your patience";

dt = getDataTable(1000000);

////-------to try percentage ...

//int iMax = 100000;

//for (int i = 0; i < iMax; i++)

//{

// backgroundWorker1.ReportProgress

// ((i * 100) / (iMax - 1));

//}

////-------------------------

e.Result = dt;

toolStripStatusLabel1.Text = "Please, wait ...";

}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

{

////-------to try percentage ...

//toolStripProgressBar1.Value = e.ProgressPercentage;

//toolStripStatusLabel1.Text = "Loading ... " +

// e.ProgressPercentage.ToString() + "%";

////-------------------------

}

private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)

{

toolStripProgressBar1.Value = 100;

dataGridViewCities.DataSource = e.Result;

toolStripStatusLabel1.Text = "";

toolStripProgressBar1.Value = 0;

timer1.Stop();

toolStripStatusLabelTime.Text = "";

}

private DataTable getDataTable(int Rows)

{

GetData.GetDataHelp getData = new GetData.GetDataHelp();

return (getData.getDataSetCities(Rows).Tables[0]);

}

private void timer1_Tick(object sender, EventArgs e)

{

TimeSpan ts = DateTime.Now.Subtract(startDate);

string sTime =" ..." + ts.Minutes.ToString("00") +

":" + ts.Seconds.ToString("00") +

":" + ts.Milliseconds.ToString("000");

toolStripStatusLabelTime.Text = sTime;

if (toolStripProgressBar1.Value ==

toolStripProgressBar1.Maximum)

{

toolStripProgressBar1.Value = 0;

}

toolStripProgressBar1.PerformStep();

}

}

}


现在,如果我们运行我们的工程,我们会看到:


图7.


加载结束之后:


图8.


结论


我希望这篇文中可以帮助你在你的.NET 2.0程序中使用BakcgroundWorker组件以便执行一些耗时的操作。


祝好运与编程同在!


--------------

译者的话,这是我在译言翻译的第一篇。从我的角度讲,我是不赞同一个看不懂这篇文章原文的人做程序员的。不过我天生不爱放弃,既然接下了翻译任务,就完成了它吧。

作者超喜欢用we/our,我酌情删去了一些,不过还是有太多的“我们”。

没有评论: