實戰.Net多執行緒(四)

草上爬發表於2008-08-22
利用BackgroundWorker進行後臺操作並在另一個單獨的窗體中顯示操作進度稍微有些複雜,但是我們已經有了些許基礎,所以繼續上一篇博文然後對原始碼進行升級!

先看下button5_Click的程式碼:

        private Form2 m_frmProgress = null;
        private BackgroundWorker m_AsyncWorker2 = null;
        private void button5_Click(object sender, EventArgs e)
        {
            if (m_AsyncWorker2 == null)
            {
                button5.Enabled = false;
                m_AsyncWorker2 = new BackgroundWorker();
                m_AsyncWorker2.WorkerReportsProgress = true;
                m_AsyncWorker2.WorkerSupportsCancellation = true;
                m_AsyncWorker2.DoWork += new DoWorkEventHandler(m_AsyncWorker2_DoWork);
                m_AsyncWorker2.ProgressChanged += new ProgressChangedEventHandler(m_AsyncWorker2_ProgressChanged);
                m_AsyncWorker2.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_AsyncWorker2_RunWorkerCompleted);
                m_frmProgress = new Form2(m_AsyncWorker2);
                m_AsyncWorker2.RunWorkerAsync();
                m_frmProgress.ShowDialog(this);
            }
        }

和上一篇博文的button4_Click內容幾乎一樣,只是多了關於進度窗體Form2的操作。我們首先宣告Form2型別的變數m_frmProgress,但不例項化,然後常規似的對BackgroundWorker的屬性和方法進行賦值,在執行非同步操作之前我們需要例項化進度窗體。對於進度窗體的建構函式我們做了改造,在建立Form2例項的同時傳遞BackgroundWorker,這個傳遞實際上是引用傳遞,也就是說傳遞的是m_AsyncWorker2的地址(.Net又將此行為隱藏了!),這樣我們就可以在Form2裡對BackgroundWorker進行控制,例如呼叫BackgroundWorker.CancelAsync()。

因為button5_Click方法內的程式碼是非阻塞的,所以呼叫完m_AsyncWorker2.RunWorkerAsync()非同步方法後m_frmProgress.ShowDialog(this)得以立刻執行,進度窗體就模式化的顯示出來了。

在進度窗體Form2中我們需要放置一個label、一個progressbar和一個button,建立一個BackgroundWorker型別的變數m_bw,用於接受透過Form2的建構函式傳遞過來的BackgroundWorker。button的單擊用於取消後臺操作,亦即呼叫BackgroundWorker.CancelAsync(),隨即關閉Form2。我們的版本還允許直接單擊窗體標題欄上的關閉按鈕來終止後臺操作並關閉窗體。Form2的原始碼如下:

    public partial class Form2 : Form
    {
        private BackgroundWorker m_bw = null;

        public Form2(BackgroundWorker bw)
        {
            InitializeComponent();
            m_bw = bw;
        }

        private void Form2_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (m_bw.IsBusy)
            {
                m_bw.CancelAsync();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            m_bw.CancelAsync();
            this.DialogResult = DialogResult.Cancel;
        }
    }

BackgroundWorker.DoWork事件則與前一篇博文的內容幾乎完全一致,只不過需要注意一點,因為建立窗體肯定需要時間,立刻開始耗時操作並報告進度時進度窗體也許還未建立完畢,所以我們最好等待些許毫秒。

報告進度的操作也與前篇博文內容差不多,只不過progressbar和label是在另外一個窗體上。那麼透過界定m_frmProgress可以訪問嗎?例如:

m_frmProgress.progressBar1.Value = e.ProgressPercentage;
m_frmProgress.label1.Text = e.ProgressPercentage.ToString() + "%";

這樣做編譯器肯定會報錯,不過沒關係,我們只要在VS IDE的解決方案資源管理器中展開Form2.cs,並開啟Form2.Designer.cs就會看到問題所在。微軟不希望初級開發者隨意對*.Designer.cs進行改動,所以無論看到什麼警告我們都要做到:不要理會他!我們不是菜鳥!

private System.Windows.Forms.Label label1;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button button1;

原來窗體上的控制元件的訪問級別都是private!那好吧,我們就把它改成public唄。

到這裡我們來看看自己的BackgroundWorker例項的相關原始碼:

        private void m_AsyncWorker2_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bwAsync = sender as BackgroundWorker;
            int iCount = new Random().Next(20, 50);
            Thread.Sleep(100); //等待,以便frmProgress建立完畢!!!
            for (int i = 0; i < iCount; i++)
            {
                Thread.Sleep(100);
                bwAsync.ReportProgress(Convert.ToInt32(i * (100.0 / iCount)));
                if (bwAsync.CancellationPending)
                {
                    Thread.Sleep(1200);
                    e.Cancel = true;
                    return;
                }
            }
            bwAsync.ReportProgress(100);
        }

        private void m_AsyncWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            m_frmProgress.progressBar1.Value = e.ProgressPercentage;
            m_frmProgress.label1.Text = e.ProgressPercentage.ToString() + "%";
        }

        private void m_AsyncWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (m_frmProgress != null)
            {
                m_frmProgress.Close();
                m_frmProgress = null;
            }
            button5.Enabled = true;
            if (e.Error != null)
            {
                MessageBox.Show(this, e.Error.Message);
                return;
            }
            if (e.Cancelled) //Cancelled...
            {
                MessageBox.Show(this, "Operation is canceled.");
            }
            else  //Completed...
            {
                MessageBox.Show(this, "Operation complete!");
            }
            m_AsyncWorker2 = null;
        }

我們已經利用BackgroundWorker建立了一種比較專業的後臺操作前臺顯示進度的框架,有興趣的朋友可以對其進行豐富和擴充。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14325734/viewspace-432993/,如需轉載,請註明出處,否則將追究法律責任。

相關文章