http://www.mysjtu.com/page/M0/S536/536907.html
C#中多线程中窗体的操作,异步委托写起来非常麻烦,最方便的是使用BackgroundWork组件在结束执行后返回主窗口的UI线程再调用其他窗口。
在项目中,由于界面的一个点击操作要处理很多事情,需要时间很久,这时整个界面就处理停滞状态,不知情的以为系统死掉了。这时候,要能给用户提供一些提示,让用户知道系统还活着并且在努力的工作,不会直接到资源管理器中杀掉进程了。由于C#是初学,通过在网上查资料实现了该功能,决定针对这个方面做个笔记。
Backgroundworker类的介绍
从字面可以理解到该类是在后台处理类,可以在单独的线程中处理比较耗时的操作,比如,上传、下载、系统初始化、资源加载、数据加载等。如果项目中需要这些功能并且不希望看到界面处理停止无响应状态,那么BackgroundWorker类可以解决这个问题。
BackgroundWorker类继承自Component。因此也是工具箱中一个组件,从“工具箱”的“组件”选项卡中拖到窗体上。如果在Window窗体设计器中使用该组件,则会出现在组件栏中,并且它的属性会显示在“属性”窗口中。
属性:
WorkerReportsProgress:指示该BackgroundWorker是否支持报告进度更新。
WorkerSupportsCancellation:指示该BackgroundWorker是否支持异步取消。
事件:
DoWork:当调用RunWorkerAsync时发生,在该事件处理程序中调用那些“耗时”的处理逻辑操作。要确保此操作中不处理任何与界面相关的对象。比如Label的赋值,进度条的赋值等。与界面的操作需要放到下面两个事件中处理。
ProgressChanged:当调用ReportProgress时发生,该事件对收到的进度更新做相应的处理操作,比如显示进度条的值或当进度为多少时给予什么提示等。
RunWorkerCompleted:当后台操作已完成、被取消或引发异常时发生。该事件主要做一些收尾工作,比如将弹出的提示窗体关闭或是当后台处理完改变某些限制变量的值等。
下面通过几个例子来说明:
场景:
1, 在同一个窗体中处理后台耗时逻辑并给予进度提示。实现后台线程的启动、操作完成后的回调、后台线程的取消和进度的更新报告等。
2, 在主窗体中处理耗时逻辑,在等待窗体中给予进度提示。实现后台线程的启动、操作完成后的回调、后台线程的取消和进度的更新报告等。
总体项目介绍:
1, 新建一个Window窗体应用程序。命名:BackgroundWorkerTest。
2, 右键--添加—Window窗体,命名:AllInOneForm,该窗体完成第一个场景。
3, 右键—添加—Window窗体,命名:WaitingForm,修改自动生成的Form1为MainForm,这两个窗体完成第二个场景。
由于MainForm是启动窗体,为了完成2个场景,在该窗体中添加2个按钮:
一个命名为:新窗体完成异步操作。用于弹出AllInOneForm完成第一个场景。
一个命名为:本窗体启动等待界面。用于弹出WaitingForm完成第二个场景。
第一个场景实现:
1, 双击AllInOneForm窗体,打开。
2, 从工具箱拖拽backgroundWorker组件到界面上,并修改其属性为:
Name:AllInOneBackgroundWorker
WorkerReportsProgress:true
WorkerSupportsCancellation:true
3, 从工具箱拖拽2个Button、1个Label,1个progressBar并进行如下设置:
Button1:Name:BtStart,Text:启动。
Button2:Name:BtStop,Text:停止。
Label1:Name:LbTip
progressBar1:Name:AllInOneProgressBar
3.5相应函数指定 (add by zcl )
确保在DoWork的事件处理程序中不操作任何用户资源分类界面对象。而应该通过ProgressChanged不时和RunWorkerCompleted事件与用户资源分类界面进行通信
private void Main06_Load(object sender, EventArgs e)
{
AllInOneBackgroundWorker.DoWork += new DoWorkEventHandler(AllInOneBackgroundWorker_DoWork);
AllInOneBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(ProgessChanged);
AllInOneBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(CompleteWork);
}
4, 双击“启动”按钮,添加启动后台线程事件。
1
2
3
4
5
6
7
8
9
|
</pre> private void BtStart_Click( object sender, EventArgs e) { if (AllInOneBackgroundWorker.IsBusy!= true ) { AllInOneBackgroundWorker.RunWorkerAsync(); } } <pre> |
5, 添加后台需要做的处理,这里以for循环10个数,每次停止500毫秒,并在此报告进度。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
</pre> private void AllInOneBackgroundWorker_DoWork( object sender,DoWorkEventArgs e) { BackgroundWorker allInOneWorker = sender as BackgroundWorker; for ( int i = 1; i <= 10; i++) { if (allInOneWorker.CancellationPending == true ) { e.Cancel = true ; break ; } else { System.Threading.Thread.Sleep(500); allInOneWorker.ReportProgress(i * 10);} } } <pre> |
6, 添加进度更新操作
1
2
3
4
5
6
7
|
private void AllInOneBackgroundWorker_ProgressChanged( object sender, ProgressChangedEventArgs e> { LbTip.Text = (e.ProgressPercentage.ToString() + "%" ); AllInOneProgressBar.Value = e.ProgressPercentage; } <pre> |
由于要操作的控件在本窗体内,因此可以这样直接调用,在第二个场景中就不能这样调用了。
7, 添加后台处理完成操作
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
private void AllInOneBackgroundWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled == true ) { LbTip.Text = "已取消!" ; } else if (e.Error != null ) { LbTip.Text = "发生异常: " + e.Error.Message; } else { LbTip.Text = "已完成!" ; } } |
这里同上可以直接调用。
8, 双击“停止”按钮,添加停止后台线程事件。
1
2
3
4
5
6
7
|
private void BtStop_Click( object sender, EventArgs e) { if (AllInOneBackgroundWorker.WorkerSupportsCancellation == true ) { AllInOneBackgroundWorker.CancelAsync(); } } |
运行效果: