在Silverlight应用中,我们可能会遇到一些操作(如:下载某个资源或某些数据库事务或完成某些特定动作),这些操作耗时较长,容易造成“用户界面停止响应”的现象。如果您需要能进行响应的用户界面,而且必须执行耗时操作,则可以使用 BackgroundWorker 类方便地解决问题。 BackgroundWorker 类是.net 2.0里新增的一个类。 使用 BackgroundWorker 类,我们可以在 Silverlight 用户界面中指示操作进度、完成和取消情况。例如,可以查看后台操作是已完成还是已取消并为用户显示消息。
一、BackgroundWorker类的基本了解
涉及的命名空间
using
System.ComponentModel;
它的几个属性:
CancellationPending——指示应用程序是否已请求取消后台操作。
IsBusy——指示 BackgroundWorker 是否正在运行异步操作
WorkerReportsProgress——该值指示 BackgroundWorker 能否报告进度更新
WorkerSupportsCancellation——该值指示 BackgroundWorker 是否支持异步取消
它的主要事件:
DoWork——调用 RunWorkerAsync 时发生。 注意确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。
ProgressChanged——调用 ReportProgress 时发生。
RunWorkerCompleted——当后台操作已完成、被取消或引发异常时发生。
它的有关方法:
CancelAsync——请求取消挂起的后台操作
ReportProgress——引发 ProgressChanged 事件
RunWorkerAsync——开始执行后台操作
二、示例
下面,我们就使用这个类来完成一个特定操作:
用于执行从1累加到6.在累加过程中,SUM值会不断更新,当累加到6后,BackgroundWorker会在成功执行结束后调用一段程序来输出最终的运行信息。
新建一个名为:SLBackgroundWorker的Silverlight应用程序,如图:
1、创建用户界面:
Page.xaml代码如下:
<
UserControl x:Class
=
"
SLBackgroundWorker.Page
"
xmlns
=
"
http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x
=
"
http://schemas.microsoft.com/winfx/2006/xaml
"
Width
=
"
400
"
Height
=
"
350
"
>
<
Grid x:Name
=
"
LayoutRoot
"
Background
=
"
White
"
>
<
StackPanel Orientation
=
"
Vertical
"
Width
=
"
400
"
Height
=
"
350
"
Background
=
"
Yellow
"
>
<
TextBlock x:Name
=
"
txtDisplay
"
FontSize
=
"
24
"
TextAlignment
=
"
Center
"
Margin
=
"
10
"
Text
=
"
当前无内容
"
Height
=
"
100
"
/>
<
Button x:Name
=
"
btnRun
"
Content
=
"
从1到6累加
"
Click
=
"
OnRun
"
Margin
=
"
10
"
Height
=
"
50
"
/>
<
Button x:Name
=
"
btnCancel
"
Content
=
"
取 消
"
Click
=
"
OnCancel
"
Margin
=
"
10
"
Height
=
"
50
"
/>
<
TextBlock x:Name
=
"
txtUserState
"
FontSize
=
"
12
"
TextAlignment
=
"
Center
"
Text
=
"
显示ReportProgress方法传回的userState值
"
/>
<
TextBlock x:Name
=
"
txtEdoArgs
"
FontSize
=
"
12
"
TextAlignment
=
"
Center
"
Text
=
"
显示RunWorkerAsync()方法调用时传入的参数
"
/>
</
StackPanel
>
</
Grid
>
</
UserControl
>
效果如下图
2、使用BackgroundWorker类
在此我们分四个步骤来操作
2.1、声明一个BackgroundWorker类实例:若要在后台运行操作,请创建一个 BackgroundWorker。可以侦听报告操作进度并在操作完成时发出信号的事件。
worker
=
new
System.ComponentModel.BackgroundWorker();
2.2、相关事件的定义
若要设置后台操作,请为 DoWork 事件添加事件处理程序,在此事件处理程序中调用耗时的操作。
Code
#region 当前BackgroundWorker所执行操作
private void OnDoWork(object sender,DoWorkEventArgs e)
{
//DoWorkEventArgs.Argument - RunWorkerAsync(object argument)传递过来的参数
//DoWorkEventArgs.Cancel - 取消操作
//DoWorkEventArgs.Result - 操作的结果。将传递到 RunWorkerCompleted 所指定的方法
//BackgroundWorker.ReportProgress(int percentProgress, object userState) - 向 ProgressChanged 汇报操作的完成进度
// int percentProgress - 操作完成的百分比 1% - 100%
// object userState - 传递到 ProgressChanged 的参数
//获取在 worker.RunWorkerAsync()方法中传来的参数
//在此处,我们把它赋值给了一个全局变量,并在OnProgressChanged过程中显示出来
eStrAll = e.Argument.ToString();
int Value = 0;
//如果Value还没有加到6,并且应用程序还没有请求取消后台操作,则继续本循环
while (Value <= 6 && !worker.CancellationPending)
{
Thread.Sleep(1000); //挂起1秒钟
Value++;
#region 调用方法一:不传递UserState值
worker.ReportProgress(Value);
#endregion
#region 调用方法一:要传递UserState值
// worker.ReportProgress(Value, "传来的UserState值 " + Value.ToString()); //此处还可以传递userState(状态对象)到RunWorkerAsync,并在OnProgressChanged中获取
#endregion
}
//如果应用程序请求取消后台操作,则取消
if (worker.CancellationPending)
{
e.Cancel = true;
}
#region 可以在此处设置e.Result的值,并在OnWorkCompleted事件中获取它的值
e.Result = "操作已完成";
#endregion
}
#endregion
若要收到进度更新的通知,请处理 ProgressChanged 事件。
Code
#region 异步操作进度事件
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
// ProgressChangedEventArgs.ProgressPercentage - ReportProgress 传递过来的操作完成的百分比
// ProgressChangedEventArgs.UserState - ReportProgress 传递过来的参数
txtDisplay.Text = e.ProgressPercentage.ToString(); //显示当前进度
#region 如果在OnDoWork中使用的是调用方法二,则在此可获取e.UserState值
//在此处获取从OnDoWork中的 worker.ReportProgress方法传回的userState值
//txtUserState.Text = e.UserState.ToString();
#endregion
#region 显示RunWorkerAsync()方法调用时传入的参数
this.txtEdoArgs.Text = eStrAll;
#endregion
}
#endregion
若要在操作完成时收到通知,请处理 RunWorkerCompleted 事件。
Code
#region 操作成功完成时的处理事件
private void OnWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//RunWorkerCompletedEventArgs.Error - DoWork 时产生的错误
//RunWorkerCompletedEventArgs.Cancelled - 后台操作是否已被取消
//RunWorkerCompletedEventArgs.Result - DoWork 的结果
#region 进行完成时的相关判断与处理
if (e.Error !=null)
{
txtDisplay.Text = "调用失败";
}
else
{
if(e.Cancelled)
{
txtDisplay.Text = "已经取消本次操作!";
}
else
{
txtDisplay.Text = e.Result.ToString();// 可以在此处获取e.Result的值(它的值在OnDoWork中进行了设置)
}
}
#endregion
}
#endregion
若要 取消异步操作,则使用CancelAsync方法。
#region
取消异步操作
private
void
OnCancel(
object
sender, RoutedEventArgs e)
{
//
取消异步操作
worker.CancelAsync();
}
#endregion
2.3、相关事件的绑定和操作设置
#region
步骤二:相关事件的定义、绑定和操作设置
//
当前BackgroundWorker所执行的操作
worker.DoWork
+=
OnDoWork;
//
绑定异步操作进度的事件
worker.ProgressChanged
+=
OnProgressChanged;
//
绑定操作成功完成的处理事件
worker.RunWorkerCompleted
+=
OnWorkCompleted;
//
是否报告进度更新
worker.WorkerReportsProgress
=
true
;
//
是否支持异步取消
worker.WorkerSupportsCancellation
=
true
;
#endregion
2.4、开始执行后台操作
若要启动后台操作,请调用 RunWorkerAsync 方法。
#region
步骤三:开始执行后台操作
//
IsBusy - 指定的 BackgroundWorker 是否正在后台操作
//
RunWorkerAsync(object argument) - 开始在后台线程执行指定的操作
//
object argument - 需要传递到 DoWork 的参数
if
(
!
worker.IsBusy)
worker.RunWorkerAsync(
"
方法RunWorkerAsync调用时传递的参数
"
);
#endregion
Page.xaml.cs全部代码如下:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Net;
using
System.Windows;
using
System.Windows.Controls;
using
System.Windows.Documents;
using
System.Windows.Input;
using
System.Windows.Media;
using
System.Windows.Media.Animation;
using
System.Windows.Shapes;
using
System.ComponentModel;
//
必须要引入此命名空间才能使用BackgroundWorker类
using
System.Threading;
//
必须要引入此命名空间才能执行 Sleep(1000)挂起1秒钟操作
namespace
SLBackgroundWorker
{
public partial class Page : UserControl
{
private BackgroundWorker worker;
string eStrAll = ""; //用于保存调用RunWorkerAsync()方法时的传参,此传参在OnDoWork()方法中截取到
public Page()
{
InitializeComponent();
}
private void OnRun(object sender, RoutedEventArgs e)
{
步骤一:声明一个BackgroundWorker类实例#region 步骤一:声明一个BackgroundWorker类实例
worker =new System.ComponentModel.BackgroundWorker();
#endregion
步骤二:相关事件的定义、绑定和操作设置#region 步骤二:相关事件的定义、绑定和操作设置
//当前BackgroundWorker所执行的操作
worker.DoWork += OnDoWork;
//绑定异步操作进度的事件
worker.ProgressChanged += OnProgressChanged;
//绑定操作成功完成的处理事件
worker.RunWorkerCompleted += OnWorkCompleted;
//是否报告进度更新
worker.WorkerReportsProgress = true;
//是否支持异步取消
worker.WorkerSupportsCancellation = true;
#endregion
步骤三:开始执行后台操作#region 步骤三:开始执行后台操作
// IsBusy - 指定的 BackgroundWorker 是否正在后台操作
// RunWorkerAsync(object argument) - 开始在后台线程执行指定的操作
// object argument - 需要传递到 DoWork 的参数
if (!worker.IsBusy)
worker.RunWorkerAsync("方法RunWorkerAsync调用时传递的参数");
#endregion
}
当前BackgroundWorker所执行操作#region 当前BackgroundWorker所执行操作
private void OnDoWork(object sender,DoWorkEventArgs e)
{
//DoWorkEventArgs.Argument - RunWorkerAsync(object argument)传递过来的参数
//DoWorkEventArgs.Cancel - 取消操作
//DoWorkEventArgs.Result - 操作的结果。将传递到 RunWorkerCompleted 所指定的方法
//BackgroundWorker.ReportProgress(int percentProgress, object userState) - 向 ProgressChanged 汇报操作的完成进度
// int percentProgress - 操作完成的百分比 1% - 100%
// object userState - 传递到 ProgressChanged 的参数
//获取在 worker.RunWorkerAsync()方法中传来的参数
//在此处,我们把它赋值给了一个全局变量,并在OnProgressChanged过程中显示出来
eStrAll = e.Argument.ToString();
int Value = 0;
//如果Value还没有加到6,并且应用程序还没有请求取消后台操作,则继续本循环
while (Value <= 6 && !worker.CancellationPending)
{
Thread.Sleep(1000); //挂起1秒钟
Value++;
调用方法一:不传递UserState值#region 调用方法一:不传递UserState值
worker.ReportProgress(Value);
#endregion
调用方法一:要传递UserState值#region 调用方法一:要传递UserState值
// worker.ReportProgress(Value, "传来的UserState值 " + Value.ToString()); //此处还可以传递userState(状态对象)到RunWorkerAsync,并在OnProgressChanged中获取
#endregion
}
//如果应用程序请求取消后台操作,则取消
if (worker.CancellationPending)
{
e.Cancel = true;
}
可以在此处设置e.Result的值,并在OnWorkCompleted事件中获取它的值#region 可以在此处设置e.Result的值,并在OnWorkCompleted事件中获取它的值
e.Result = "操作已完成";
#endregion
}
#endregion
异步操作进度事件#region 异步操作进度事件
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
// ProgressChangedEventArgs.ProgressPercentage - ReportProgress 传递过来的操作完成的百分比
// ProgressChangedEventArgs.UserState - ReportProgress 传递过来的参数
txtDisplay.Text = e.ProgressPercentage.ToString(); //显示当前进度
如果在OnDoWork中使用的是调用方法二,则在此可获取e.UserState值#region 如果在OnDoWork中使用的是调用方法二,则在此可获取e.UserState值
//在此处获取从OnDoWork中的 worker.ReportProgress方法传回的userState值
//txtUserState.Text = e.UserState.ToString();
#endregion
显示RunWorkerAsync()方法调用时传入的参数#region 显示RunWorkerAsync()方法调用时传入的参数
this.txtEdoArgs.Text = eStrAll;
#endregion
}
#endregion
操作成功完成时的处理事件#region 操作成功完成时的处理事件
private void OnWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//RunWorkerCompletedEventArgs.Error - DoWork 时产生的错误
//RunWorkerCompletedEventArgs.Cancelled - 后台操作是否已被取消
//RunWorkerCompletedEventArgs.Result - DoWork 的结果
进行完成时的相关判断与处理#region 进行完成时的相关判断与处理
if (e.Error !=null)
{
txtDisplay.Text = "调用失败";
}
else
{
if(e.Cancelled)
{
txtDisplay.Text = "已经取消本次操作!";
}
else
{
txtDisplay.Text = e.Result.ToString();// 可以在此处获取e.Result的值(它的值在OnDoWork中进行了设置)
}
}
#endregion
}
#endregion
取消异步操作#region 取消异步操作
private void OnCancel(object sender, RoutedEventArgs e)
{
//取消异步操作
worker.CancelAsync();
}
#endregion
}
}
运行效果如下:
说明:
1、您必须非常小心,确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。
2、如果后台操作需要参数,请在调用 RunWorkerAsync 时给出参数。在 DoWork 事件处理程序内部,可以从DoWorkEventArgs提取该参数。
前往:Silverlight学习笔记清单
本文程序在Silverlight2.0和VS2008环境中调试通过。本文参照了部分网络资料,希望能够抛砖引玉,大家共同学习。
(转载本文请注明出处)