首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

別再忘了 BackgroundWorker:WinForms 多線程進度報告的經典之選

     編輯:關於C#

還記得 BackgroundWorker 嗎?在 async/await 與 Task 稱霸非同步開發的今天,這個 .NET 2.0 時代的老將似乎被許多開發者遺忘在工具箱的角落。其實,針對「後台運算需即時回報進度」這類經典 WinForms 場景,BackgroundWorker 依然是最直覺、寫法最乾淨的選擇之一。

情境回顧:進度條在另一個視窗怎麼辦?

有位開發者遇到經典難題:長時間運算需在獨立視窗顯示進度條。若直接 ShowDialog() 會阻塞 UI 執行緒,導致運算無法啟動;若先啟動運算再 ShowDialog(),又得處理跨執行緒更新 UI 的委派麻煩。她詢問三個核心問題:

  1. 模態對話框阻塞怎麼解?
  2. 跨視窗、跨執行緒控制控件怎麼寫?
  3. 後台任務還有更簡單寫法嗎?

BackgroundWorker 三步曲,輕鬆破解

BackgroundWorker 專為「需回報進度、支援取消」的後台作業設計,核心事件只有三個:

  • DoWork:放入耗時運算邏輯,呼叫 ReportProgress() 送出進度。
  • ProgressChanged:自動在 UI 執行緒觸發,安全更新進度條、標籤。
  • RunWorkerCompleted:運算結束(或取消、拋錯)時觸發,收尾關閉視窗、復原按鈕狀態。

關鍵屬性別忘記勾:

  • WorkerReportsProgress = true(否則報告進度會拋例外)
  • WorkerSupportsCancellation = true(若需支援 CancelAsync())

完整實作範例

下方程式碼示範:點擊按鈕 → 啟動 BackgroundWorker → ShowDialog() 顯示進度視窗 → 背景產生大量暫存檔並即時回報進度 → 完成自動關閉視窗彈出訊息。

using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;

namespace MyApp {
    public partial class Form1 : Form {
        static string SaveDir = string.Empty;
        private FormProgress fpro = null;

        public Form1() {
            InitializeComponent();
            fpro = new FormProgress(); // 另一個只含 ProgressBar 與 Label 的 Form
        }

        // 1. 後台運算
        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 + ".tmp");
                    using (var stream = File.Create(fileName)) {
                        int n = 0, maxByte = 8 * 1024 * 1024; // 8MB
                        while (n < maxByte) {
                            rand.NextBytes(buffer);
                            stream.Write(buffer, 0, buffer.Length);
                            n += buffer.Length;
                        }
                    }
                } catch { /* 忽略單檔錯誤 */ }
                finally {
                    backgroundWorker1.ReportProgress(i + 1); // 回報進度
                }
            }
        }

        // 2. UI 更新(自動在 UI 執行緒)
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
            fpro.lblText.Text = $"已生成 {e.ProgressPercentage} 個檔案。";
            fpro.progressBar1.Value = e.ProgressPercentage;
        }

        // 3. 完成收尾
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            button1.Enabled = true;
            fpro.Hide();
            MessageBox.Show("操作完成。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        // 啟動按鈕
        private void button1_Click(object sender, EventArgs e) {
            if (!Directory.Exists(SaveDir)) return;
            button1.Enabled = false;
            int count = Convert.ToInt32(nmbud.Value);
            fpro.progressBar1.Minimum = 0;
            fpro.progressBar1.Maximum = count;
            fpro.progressBar1.Value = 0;
            backgroundWorker1.RunWorkerAsync(count); // 先啟動背景
            fpro.ShowDialog(this);                   // 再顯示模態視窗,不阻塞背景
        }

        // 選擇資料夾
        private void btnBrowsFd_Click(object sender, EventArgs e) {
            using (var fd = new FolderBrowserDialog { RootFolder = Environment.SpecialFolder.Desktop }) {
                if (fd.ShowDialog() == DialogResult.OK) SaveDir = fd.SelectedPath;
            }
        }
    }
}

設計器拖曳 BackgroundWorker

執行畫面示意

為什麼 2024 年還值得用它?

  • 零委派煩惱:ProgressChanged、RunWorkerCompleted 自動切回 UI 執行緒,不需 Invoke/BeginInvoke。
  • 語義清晰:三個事件對應「做事、報進度、收尾」,程式碼可讀性極高。
  • 取消內建:CancellationPending 搭配迴圈判斷,取消邏輯一行搞定。
  • 設計器支援:工具箱拖拉即用,適合快速原型或維護舊專案。

當然,若專案已全面轉向 Task + IProgress<T> 或 async/await,BackgroundWorker 並非強迫選項。但下次面對「WinForms 要在另一個視窗跑進度條」這類需求時,別忘了工具箱裡那個灰色的元件——它或許能替你省下幾十行委派程式碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved