C#中使用BackgroundWorker 实现后台操作窗体
编程的时候常常遇到需要处理很长时间的函数等,这时可能会导致用户界面 (UI) 似乎处于停止响应状态。在VS2005里可以使用 BackgroundWorker 类来杜绝这种UI假死的现象。
DoWork 事件:处理程序中调用耗时的操作。
RunWorkerAsync 事件:若要启动该操作。
ProgressChanged 事件:收到进度更新通知。
RunWorkerCompleted 事件在操作完成时收到通知。
下面通过一个程序说明,程序来自msdn(Link),有所删减。
public partial class FormFibonacci : Form
{
private int numberToCompute = 0;
private int highestPercentageReached = 0 ;
public FormFibonacci ()
{
InitializeComponent ();
}
// 占时计算函数
long ComputeFibonacci (int n, BackgroundWorker worker , DoWorkEventArgs e)
{
// The parameter n must be >= 0 and <= 91.
// Fib(n), with n > 91, overflows a long.
if ((n < 0) || (n > 91 )) {
throw new ArgumentException (
"value must be >= 0 and <= 91", "n");
}
long result = 0;
// Abort the operation if the user has canceled.
// Note that a call to CancelAsync may have set
// CancellationPending to true just after the
// last invocation of this method exits, so this
// code will not have the opportunity to set the
// DoWorkEventArgs.Cancel flag to true. This means
// that RunWorkerCompletedEventArgs.Cancelled will
// not be set to true in your RunWorkerCompleted
// event handler. This is a race condition.
if (worker .CancellationPending ) {
e.Cancel = true;
} else {
if (n < 2) {
result = 1;
} else {
result = ComputeFibonacci(n - 1, worker, e) +
ComputeFibonacci(n - 2, worker, e);
}
// 报告进度
int percentComplete =
( int)((float) n / ( float )numberToCompute * 100 );
if (percentComplete > highestPercentageReached) {
highestPercentageReached = percentComplete;
worker.ReportProgress (percentComplete);
}
}
return result;
}
private void backgroundWorker1_DoWork (object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e.Result = ComputeFibonacci ((int)e.Argument , worker, e );
}
private void btnStart_Click( object sender, EventArgs e)
{
numberToCompute = (int)numericUpDown1 .Value ;
highestPercentageReached = 0;
//Start the asynchronous operation.
//注意:参数从这里传过去
backgroundWorker1.RunWorkerAsync (numberToCompute);
}
private void btnCancel_Click (object sender, EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1 .CancelAsync();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e. Error != null) {
MessageBox.Show(e.Error. Message);
} else if (e .Cancelled) {
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
resultLabel .Text = "Canceled" ;
} else {
// Finally, handle the case where the operation
// succeeded.
resultLabel.Text = e.Result. ToString();
}
}
private void backgroundWorker1_ProgressChanged (object sender, ProgressChangedEventArgs e)
{
//设置进度条
progressBar1.Value = e.ProgressPercentage ;
}
}
{
private int numberToCompute = 0;
private int highestPercentageReached = 0 ;
public FormFibonacci ()
{
InitializeComponent ();
}
// 占时计算函数
long ComputeFibonacci (int n, BackgroundWorker worker , DoWorkEventArgs e)
{
// The parameter n must be >= 0 and <= 91.
// Fib(n), with n > 91, overflows a long.
if ((n < 0) || (n > 91 )) {
throw new ArgumentException (
"value must be >= 0 and <= 91", "n");
}
long result = 0;
// Abort the operation if the user has canceled.
// Note that a call to CancelAsync may have set
// CancellationPending to true just after the
// last invocation of this method exits, so this
// code will not have the opportunity to set the
// DoWorkEventArgs.Cancel flag to true. This means
// that RunWorkerCompletedEventArgs.Cancelled will
// not be set to true in your RunWorkerCompleted
// event handler. This is a race condition.
if (worker .CancellationPending ) {
e.Cancel = true;
} else {
if (n < 2) {
result = 1;
} else {
result = ComputeFibonacci(n - 1, worker, e) +
ComputeFibonacci(n - 2, worker, e);
}
// 报告进度
int percentComplete =
( int)((float) n / ( float )numberToCompute * 100 );
if (percentComplete > highestPercentageReached) {
highestPercentageReached = percentComplete;
worker.ReportProgress (percentComplete);
}
}
return result;
}
private void backgroundWorker1_DoWork (object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e.Result = ComputeFibonacci ((int)e.Argument , worker, e );
}
private void btnStart_Click( object sender, EventArgs e)
{
numberToCompute = (int)numericUpDown1 .Value ;
highestPercentageReached = 0;
//Start the asynchronous operation.
//注意:参数从这里传过去
backgroundWorker1.RunWorkerAsync (numberToCompute);
}
private void btnCancel_Click (object sender, EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1 .CancelAsync();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e. Error != null) {
MessageBox.Show(e.Error. Message);
} else if (e .Cancelled) {
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
resultLabel .Text = "Canceled" ;
} else {
// Finally, handle the case where the operation
// succeeded.
resultLabel.Text = e.Result. ToString();
}
}
private void backgroundWorker1_ProgressChanged (object sender, ProgressChangedEventArgs e)
{
//设置进度条
progressBar1.Value = e.ProgressPercentage ;
}
}
在程序中需要注意以下几点:
1. 必须确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。
2. 如果后台操作需要参数,要在调用 RunWorkerAsync 时给出参数。在 DoWork 事件处理程序内部,可以从 DoWorkEventArgs.Argument 属性中提取该参数。
3. 报告进度函数 ReportProgress有一个参数(或者两个,参见msdn): percentProgress 已完成的后台操作所占的百分比,范围从 0% 到 100%。
4. 注意取消操作和进度条的设置。
5. 可以使用 IsBusy 属性可以确定 BackgroundWorker 线程是否仍在运行。如果代码在主 UI 线程上(对于 Click 事件处理程序即是如此),请务必调用 System.Windows.Forms.Application.DoEvents 方法以使用户界面能够响应用户操作。在btnClick中可以有:
while (this.backgroundWorker1.IsBusy)
{
Application.DoEvents();
}
{
Application.DoEvents();
}
6. 在试图访问 System.ComponentModel.RunWorkerCompletedEventArgs.Result 属性或可能已受 DoWork 事件处理程序影响的任何其他对象之前,一定要在 RunWorkerCompleted 事件处理程序中检查 System.ComponentModel.AsyncCompletedEventArgs.Error 属性。
没有评论:
发表评论