2012年3月22日 星期四

C# BackgroundWorker

使用 .net  內建的元件去實現非同步作業。當一個作業需要花很長的時間去完成,大都會使用背景處理,

這樣使用者還是可以繼續其他的操作,並且 UI 介面也不會鎖死。

當然,有許多方法可以達到背景作業的效果,這邊使用 backgroundworker 元件測試。


Example

public partial class Form1 : Form {

    //宣告 BW 變數
    private BackgroundWorker _BW;

    //宣告 _Limit 變數,並且設定初值
    private int _Limit = 100000;

    public Form1() {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e) {
        this.btnStartBackgroundWork.Enabled = true;
        this.btnCancelBackgroundWork.Enabled = false;
    }

    private void btnStartBackgroundWork_Click(object sender, EventArgs e) {
        //關閉 btnStartBackgroundWork 按鈕
        btnStartBackgroundWork.Enabled = false;

        //開啟 btnCancelBackgroundWork 按鈕
        btnCancelBackgroundWork.Enabled = true;

        //設定 ProgressBar 最大值
        toolStripProgressBar1.Maximum = _Limit;

        //建立 BW 物件
        _BW = new BackgroundWorker();

        //設定是否支援非同步作業取消
        _BW.WorkerSupportsCancellation = true;

        //繫結 DoWork 事件,當非同步作業啟動時,會呼叫該事件
        _BW.DoWork += new DoWorkEventHandler(_BW_DoWork);

        //啟動非同步作業,並將參數傳入
        //在 DoWork 函式中,參數會由 DoWorkEventArgs 帶出
        _BW.RunWorkerAsync(0);
    }

    //非同步作業
    void _BW_DoWork(object sender, DoWorkEventArgs e) {
        //設定 BW 物件可以更新表單資訊報告進度
        _BW.WorkerReportsProgress = true;

        //繫結 ProgressChanged 事件,當 WorkerReportsProgress 屬性設為 true ,
        //會觸發該事件,同時在這麼函式下,可以更新表單資訊
        _BW.ProgressChanged += new ProgressChangedEventHandler(_BW_ProgressChanged);

        //當背景作業已完成時、取消、例外發生時皆會觸發該事件
        _BW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_BW_RunWorkerCompleted);

        //呼叫自訂函式
        DoAdd((BackgroundWorker)sender, e);
    }

    //非同步作業已完成
    void _BW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        if (e.Cancelled) // 如果非同步作業已被取消...
        {
            UpdateProgressBar("非同步作業已被取消。");

        } else // 如果非同步作業順利完成....
        {
            UpdateProgressBar("非同步作業順利完成。");
        }
    }

    private void DoAdd(BackgroundWorker worker, DoWorkEventArgs e) {
        int iComplete = (int)e.Argument;
        while (iComplete < _Limit) {

            //判斷使用者是否已經取消背景作業
            if (worker.CancellationPending == true) {

                //設定取消事件
                e.Cancel = true;

                //跳出迴圈
                break;
            }

            //呼叫這個方法,會觸發 ProgressChanged 事件            
            worker.ReportProgress(iComplete);
            
            iComplete++;

            //這邊睡一下,給緩衝去給表單的 UI 重劃,
            //系統會另外開一條執行緒去觸發事件 (_BW_ProgressChanged)。
            System.Threading.Thread.Sleep(1);
        }

    }

    //在這麼事件裡,可以存取表單的資訊
    void _BW_ProgressChanged(object sender, ProgressChangedEventArgs e) {
        //若是已經取消背景作業,就不去更新表單資訊,
        //否則會引發 [引動過程的目標傳回例外狀況]。
        if (((BackgroundWorker)sender).CancellationPending == false) {
            UpdateProgressBar(e.ProgressPercentage);
            UpdateTxtBox(e.ProgressPercentage);
        }
    }


    private void btnCancelBackgroundWork_Click(object sender, EventArgs e) {
        //取消背景作業
        this.CancelBackgroundWork();
    }

    private void CancelBackgroundWork() {
        // 在嘗試取消非同步作業前,先判斷非同步作業是否仍在執行中。
        if (_BW.IsBusy) {
            UpdateProgressBar("非同步作業計算中...");

            // 呼叫 BackgroundWorker 物件的 CancelAsync 方法來要求取消非同步作業。
            _BW.CancelAsync();
        }
    }

    //更新狀態文字
    private void UpdateProgressBar(string text) {
        toolStripStatusLabel1.Text = text;
    }

    //更新 ProgressBar
    private void UpdateProgressBar(int value) {
        toolStripProgressBar1.Value = value;
    }

    //更新 TxtBox
    private void UpdateTxtBox(int value) {
        textBox1.Text = value.ToString();
    }

    //關閉表單發生時
    private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
        //取消背景作業
        this.CancelBackgroundWork();              
    }  
    
}
輸出結果
當背景作業發生時,會不斷更新畫面,同時還是可以編輯 textbox 。

沒有留言:

張貼留言