還記得 BackgroundWorker 嗎?在 async/await 與 Task 稱霸非同步開發的今天,這個 .NET 2.0 時代的老將似乎被許多開發者遺忘在工具箱的角落。其實,針對「後台運算需即時回報進度」這類經典 WinForms 場景,BackgroundWorker 依然是最直覺、寫法最乾淨的選擇之一。
有位開發者遇到經典難題:長時間運算需在獨立視窗顯示進度條。若直接 ShowDialog() 會阻塞 UI 執行緒,導致運算無法啟動;若先啟動運算再 ShowDialog(),又得處理跨執行緒更新 UI 的委派麻煩。她詢問三個核心問題:
BackgroundWorker 專為「需回報進度、支援取消」的後台作業設計,核心事件只有三個:
ReportProgress() 送出進度。關鍵屬性別忘記勾:
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;
}
}
}
}


ProgressChanged、RunWorkerCompleted 自動切回 UI 執行緒,不需 Invoke/BeginInvoke。CancellationPending 搭配迴圈判斷,取消邏輯一行搞定。當然,若專案已全面轉向 Task + IProgress<T> 或 async/await,BackgroundWorker 並非強迫選項。但下次面對「WinForms 要在另一個視窗跑進度條」這類需求時,別忘了工具箱裡那個灰色的元件——它或許能替你省下幾十行委派程式碼。