程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 還記得BackgroundWorker嗎

還記得BackgroundWorker嗎

編輯:關於C#

打開電腦,突然想起來,昨天下午,有位仁兄和我討論過一個事,不妨拿來說說。

她說她的 牛逼程序要處理一堆東東,要弄個進度條作提示,不過進度條是在另一個窗口中的,她的想法是,在開 始處理數據時彈出進度對話框,實時顯示處理進度,當處理完成後關閉對話框。乍看起來其實不難,不 過她遇到了以下問題,故在群裡提問。

1、模態對話框的問題。

這問題好辦,一般來說, 要長時間來處理數據,應該考慮後台異步操作,用磚家的話講就是多線程。不過她在顯示窗口時調用了 ShowDialog方法,這樣代碼會一直停在那裡,直到窗口關閉。

如果是異步操作,通常來說,在啟 動後台任務後會馬上返回,這麼一來,只要把代碼的順序調一下就可以解決這問題,先啟動後台任務, 再調用ShowDialog方法,這樣一來,就算代碼停在ShowDialog那裡也不會影響後台任務的執行。

2、如何控制其他窗口中控件。

可以在在窗口類中定義公共方法來對控件進行某些操作, 之後在其他地方就可以通過這些公共方法來調控。如果是跨線程調用,應當考慮使用委托或事件來調用 。不然你學了委托和事件干嗎呢?

另一種方法就是直接把進度窗口中的ProgressBar控件聲明為 public,這樣其他類就可以輕松訪問了。

3、後台任務如何更簡單。

方法是靈活的,有很 多種。最簡單的是利用.NET 4.5和C# 5.0 中的新特性,這種方法肯定是最簡單的。第二種則是使用.NET 4 中新加的Task類來調度線程;比較傳統,在2.0時代用得最多的方法是直接用Thread類。

但是 ,有一個組件是專為後台任務而開放的,忘了沒有?——BackgroundWorker,也許有些朋友是忘了這個 組件了。現在很多人就是這樣,有了小三就忘了結發妻子,我們眾多碼農也是這樣,常常會忘本。

既有後台任務,又要報告進度,用BackgroundWorker不是更合適嗎?於是我問了她兩遍:“還記 得嗎?” 果然不出我所料,記不起來了,呵呵。

說了那麼多,估計需要實例才能解決問題,好 吧,上菜。

using System;  
using System.Collections.Generic;  
using System.ComponentModel;  
using System.Data;  
using System.Drawing;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using System.Windows.Forms;  
using System.IO;  
      
namespace MyApp  
{  
    public partial class Form1 : Form  
    {  
        static string SaveDir = string.Empty;  
        private FormProgress fpro = null;  
        public Form1()  
        {  
            InitializeComponent();  
            fpro = new FormProgress();  
        }  
      
      
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)  
        {  
            int fileCount = Convert.ToInt32(e.Argument);  
            Random rand = new Random();  
            byte[] buffer = new byte[2048];  
            for (int i = 0; i < fileCount; i++)  
            {  
                try
                {  
                    string fileName = Path.Combine(SaveDir, i.ToString() + ".tmp");  
                    using (var stream = File.Create(fileName))  
                    {  
                        int n = 0;  
                        int maxByte = 8 * 1024 * 1024;  
                        while (n < maxByte)  
                        {  
                            rand.NextBytes(buffer);  
                            stream.Write(buffer, 0, buffer.Length);  
                            n += buffer.Length;  
                        }  
                    }  
                }  
                catch
                {  
                    continue;  
                }  
                finally
                {  
                    // 報告進度  
                    this.backgroundWorker1.ReportProgress(i + 1);  
                }  
            }  
        }  
      
        private void button1_Click(object sender, EventArgs e)  
        {  
            if (Directory.Exists(SaveDir) == false)  
            {  
                return;  
            }  
            button1.Enabled = false;  
            int cout = Convert.ToInt32(this.nmbud.Value);  
            this.fpro.progressBar1.Minimum = 0;  
            this.fpro.progressBar1.Maximum = cout;  
            this.fpro.progressBar1.Value = this.fpro.progressBar1.Minimum;  
            this.backgroundWorker1.RunWorkerAsync(cout);  
            // 在開始異步操作後ShowDialog  
            // 這樣即使代碼停在那裡也不會影響後台任務的執行  
            fpro.ShowDialog(this);  
        }  
      
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)  
        {  
            int val = e.ProgressPercentage;  
            this.fpro.lblText.Text = string.Format("已生成{0}個文件。", val);  
            this.fpro.progressBar1.Value = val;  
        }  
      
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)  
        {  
            button1.Enabled = true;  
            fpro.Hide();  
            MessageBox.Show("操作完成。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);  
        }  
      
        private void btnBrowsFd_Click(object sender, EventArgs e)  
        {  
            FolderBrowserDialog fd = new FolderBrowserDialog();  
            fd.RootFolder = Environment.SpecialFolder.Desktop;  
            if (DialogResult.OK == fd.ShowDialog())  
            {  
                SaveDir = fd.SelectedPath;  
            }  
            fd.Dispose();  
        }  
    }  
}

FormProgress是放置了ProgressBar的另一個窗體,backgroundWorker1由設計器生成,在設 計窗口,可以直接從工具箱中把BackgroundWorker拖到設計器窗口上。

代碼沒有難 度,相信小姑娘能看懂的。下面我們總結一下BackgroundWorker的用法,相信很多書上都有介紹,哪怕 是抄MSDN的書。

1、如果希望報告進度,WorkerReportsProgress屬性必須為true,否則報告進度 時會異常。如果允許通過調用CancelAsync方法取消任務,WorkerSupportsCancellation屬性要為true。

2、處理DoWork事件,後台要處理的任務代碼就寫在該事件的處理方法中。

3、 ProgressChanged事件,當調用ReportProgress方法報告進度後,會引發該事件,處理該事件實時更新進 度條的顯示。

4、當任務執行完成後會引發RunWorkerCompleted事件,如果後台任務需要返回結 果,可從事件參數RunWorkerCompletedEventArgs.Result屬性中取得結果。那麼,這個結果是怎麼設置 的呢?不妨再看看前面的DoWork事件,它的事件參數DoWorkEventArgs的Result屬性,當我們的任務執行 完成時,把結果賦給該屬性,隨後引發RunWorkerCompleted事件時,會把結果傳遞到 RunWorkerCompletedEventArgs.Result屬性。

現在,我們可以看看最終的效果,是否達到預期 要求。

查看本欄目

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved