異世界


2012年4月29日 星期日

[C#] backgroundworker 元件


Introduction

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

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

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


Example
001
public partial class Form1 : Form {
002
    //宣告 BW 變數
003
    private BackgroundWorker _BW;
004
    //宣告 _Limit 變數,並且設定初值
005
    private int _Limit = 100000;
006
    public Form1() {
007
        InitializeComponent();
008
    }
009
    private void Form1_Load(object sender, EventArgs e) {
010
        this.btnStartBackgroundWork.Enabled = true;
011
        this.btnCancelBackgroundWork.Enabled = false;
012
    }
013
    private void btnStartBackgroundWork_Click(object sender, EventArgs e) {
014
        //關閉 btnStartBackgroundWork 按鈕
015
        btnStartBackgroundWork.Enabled = false;
016
        //開啟 btnCancelBackgroundWork 按鈕
017
        btnCancelBackgroundWork.Enabled = true;
018
        //設定 ProgressBar 最大值
019
        toolStripProgressBar1.Maximum = _Limit;
020
        //建立 BW 物件
021
        _BW = new BackgroundWorker();
022
        //設定是否支援非同步作業取消
023
        _BW.WorkerSupportsCancellation = true;
024
        //繫結 DoWork 事件,當非同步作業啟動時,會呼叫該事件
025
        _BW.DoWork += new DoWorkEventHandler(_BW_DoWork);
026
        //啟動非同步作業,並將參數傳入
027
        // DoWork 函式中,參數會由 DoWorkEventArgs 帶出
028
        _BW.RunWorkerAsync(0);
029
    }
030
    //非同步作業
031
    void _BW_DoWork(object sender, DoWorkEventArgs e) {
032
        //設定 BW 物件可以更新表單資訊報告進度
033
        _BW.WorkerReportsProgress = true;
034
        //繫結 ProgressChanged 事件,當 WorkerReportsProgress 屬性設為 true
035
        //會觸發該事件,同時在這麼函式下,可以更新表單資訊
036
        _BW.ProgressChanged += new ProgressChangedEventHandler(_BW_ProgressChanged);
037
        //當背景作業已完成時、取消、例外發生時皆會觸發該事件
038
        _BW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_BW_RunWorkerCompleted);
039
        //呼叫自訂函式
040
        DoAdd((BackgroundWorker)sender, e);
041
    }
042
    //非同步作業已完成
043
    void _BW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
044
        if (e.Cancelled) // 如果非同步作業已被取消...
045
        {
046
            UpdateProgressBar("非同步作業已被取消。");
047
        } else // 如果非同步作業順利完成....
048
        {
049
            UpdateProgressBar("非同步作業順利完成。");
050
        }
051
    }
052
    private void DoAdd(BackgroundWorker worker, DoWorkEventArgs e) {
053
        int iComplete = (int)e.Argument;
054
        while (iComplete < _Limit) {
055
            //判斷使用者是否已經取消背景作業
056
            if (worker.CancellationPending == true) {
057
                //設定取消事件
058
                e.Cancel = true;
059
                //跳出迴圈
060
                break;
061
            }
062
            //呼叫這個方法,會觸發 ProgressChanged 事件            
063
            worker.ReportProgress(iComplete);
064
              
065
            iComplete++;
066
            //這邊睡一下,給緩衝去給表單的 UI 重劃,
067
            //系統會另外開一條執行緒去觸發事件 (_BW_ProgressChanged)
068
            System.Threading.Thread.Sleep(1);
069
        }
070
    }
071
    //在這麼事件裡,可以存取表單的資訊
072
    void _BW_ProgressChanged(object sender, ProgressChangedEventArgs e) {
073
        //若是已經取消背景作業,就不去更新表單資訊,
074
        //否則會引發 [引動過程的目標傳回例外狀況]
075
        if (((BackgroundWorker)sender).CancellationPending == false) {
076
            UpdateProgressBar(e.ProgressPercentage);
077
            UpdateTxtBox(e.ProgressPercentage);
078
        }
079
    }
080
    private void btnCancelBackgroundWork_Click(object sender, EventArgs e) {
081
        //取消背景作業
082
        this.CancelBackgroundWork();
083
    }
084
    private void CancelBackgroundWork() {
085
        // 在嘗試取消非同步作業前,先判斷非同步作業是否仍在執行中。
086
        if (_BW.IsBusy) {
087
            UpdateProgressBar("非同步作業計算中...");
088
            // 呼叫 BackgroundWorker 物件的 CancelAsync 方法來要求取消非同步作業。
089
            _BW.CancelAsync();
090
        }
091
    }
092
    //更新狀態文字
093
    private void UpdateProgressBar(string text) {
094
        toolStripStatusLabel1.Text = text;
095
    }
096
    //更新 ProgressBar
097
    private void UpdateProgressBar(int value) {
098
        toolStripProgressBar1.Value = value;
099
    }
100
    //更新 TxtBox
101
    private void UpdateTxtBox(int value) {
102
        textBox1.Text = value.ToString();
103
    }
104
    //關閉表單發生時
105
    private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
106
        //取消背景作業
107
        this.CancelBackgroundWork();              
108
    }  
109
110