新鲜、有趣,互联生活。令狐葱。

2006/12/30

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 ;
        }
    }

 
 
在程序中需要注意以下几点:
 
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();
    }
 
6. 在试图访问 System.ComponentModel.RunWorkerCompletedEventArgs.Result 属性或可能已受 DoWork 事件处理程序影响的任何其他对象之前,一定要在 RunWorkerCompleted 事件处理程序中检查 System.ComponentModel.AsyncCompletedEventArgs.Error 属性。

没有评论: