基本屬性與模式
| 屬性 | 說明 | 常用值 |
| Minimum / Maximum | 進度範圍 | 0 / 100 |
| Value | 當前進度 | 0~Maximum |
| Step | PerformStep() 遞增量 | 1 |
| Style | 顯示樣式 | Blocks(預設)、Continuous、Marquee |
| MarqueeAnimationSpeed | Marquee 模式動畫速度(ms) | 30~100 |
基本用法:同步迴圈更新(UI 會凍結)
private void btnSync_Click(object sender, EventArgs e) {
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
progressBar1.Value = 0;
for (int i = 0; i <= 100; i++) {
progressBar1.Value = i;
Thread.Sleep(50); // 模擬工作,**會凍結 UI**,不建議實務使用
}
}
正確做法:BackgroundWorker / Task + Progress 非同步更新
方案一:BackgroundWorker (.NET 2.0+ 傳統)
private BackgroundWorker _bw;
private void Form_Load(object sender, EventArgs e) {
_bw = new BackgroundWorker {
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_bw.DoWork += Bw_DoWork;
_bw.ProgressChanged += Bw_ProgressChanged;
_bw.RunWorkerCompleted += Bw_Completed;
}
private void btnStart_Click(object sender, EventArgs e) {
if (!_bw.IsBusy) _bw.RunWorkerAsync();
}
private void Bw_DoWork(object sender, DoWorkEventArgs e) {
var bw = sender as BackgroundWorker;
for (int i = 0; i <= 100; i++) {
if (bw.CancellationPending) { e.Cancel = true; return; }
Thread.Sleep(50); // 真實工作
bw.ReportProgress(i); // 觸發 ProgressChanged(自動切回 UI 執行緒)
}
}
private void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e) {
progressBar1.Value = e.ProgressPercentage;
lblStatus.Text = $"{e.ProgressPercentage}%";
}
private void Bw_Completed(object sender, RunWorkerCompletedEventArgs e) {
if (e.Cancelled) lblStatus.Text = "已取消";
else if (e.Error != null) lblStatus.Text = "錯誤: " + e.Error.Message;
else lblStatus.Text = "完成!";
}
方案二:Task + IProgress (現代 .NET 4.5+ 推薦)
private async void btnAsync_Click(object sender, EventArgs e) {
btnAsync.Enabled = false;
progressBar1.Value = 0;
progressBar1.Style = ProgressBarStyle.Continuous;
var progress = new Progress(v => {
progressBar1.Value = v;
lblStatus.Text = $"{v}%";
});
try {
await Task.Run(() => DoHeavyWork(progress));
lblStatus.Text = "完成!";
} catch (Exception ex) {
lblStatus.Text = "錯誤: " + ex.Message;
} finally {
btnAsync.Enabled = true;
}
}
private void DoHeavyWork(IProgress progress) {
for (int i = 0; i <= 100; i++) {
Thread.Sleep(50); // 真實工作
progress.Report(i); // 自動捕獲 SynchronizationContext 回 UI 執行緒
}
}
Marquee 模式:不確定進度(如連線中、查詢中)
private void StartIndeterminate() {
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.MarqueeAnimationSpeed = 50;
}
private void StopIndeterminate() {
progressBar1.Style = ProgressBarStyle.Blocks;
progressBar1.MarqueeAnimationSpeed = 0;
progressBar1.Value = 0;
}
雙層進度條:外層總進度 + 內層當前檔案
// UI: progressOverall (總進度), progressCurrent (單檔進度)
private async Task ProcessFilesAsync(string[] files, IProgress<(int overall, int current)> progress) {
for (int i = 0; i < files.Length; i++) {
// 模擬單檔處理
for (int p = 0; p <= 100; p += 10) {
await Task.Delay(50);
progress.Report(( (i * 100) / files.Length, p ));
}
progress.Report(( (i + 1) * 100 / files.Length, 100 ));
}
}
// 使用
var progress = new Progress<(int overall, int current)>(t => {
progressOverall.Value = t.overall;
progressCurrent.Value = t.current;
});
await ProcessFilesAsync(fileList, progress);
自訂繪製:OwnerDraw 進度條(顯示文字、顏色漸層)
public class CustomProgressBar : ProgressBar {
public CustomProgressBar() {
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(PaintEventArgs e) {
Rectangle rect = ClientRectangle;
Graphics g = e.Graphics;
// 背景
using (var brush = new SolidBrush(SystemColors.Control)) g.FillRectangle(brush, rect);
// 進度填滿(漸層)
int width = (int)(rect.Width * ((double)Value / Maximum));
if (width > 0) {
Rectangle progRect = new Rectangle(0, 0, width, rect.Height);
using (var lgb = new LinearGradientBrush(progRect, Color.LimeGreen, Color.ForestGreen, LinearGradientMode.Horizontal))
g.FillRectangle(lgb, progRect);
}
// 文字置中
string text = $"{Value}%";
SizeF sz = g.MeasureString(text, Font);
float x = (rect.Width - sz.Width) / 2;
float y = (rect.Height - sz.Height) / 2;
using (var brush = new SolidBrush(Color.Black)) g.DrawString(text, Font, brush, x, y);
}
}
使用:工具箱右鍵「選擇項目」→ 加入自訂控制項,或直接在程式碼 `new CustomProgressBar()`。
常見問題與解決
| 問題 | 原因 | 解決 |
| 進度條不更新/卡住 | 長時間工作在 UI 執行緒 | 移至 BackgroundWorker/Task,用 ReportProgress/IProgress |
| Value 設定超出範圍例外 | Maximum 未設或計算錯誤 | 設值前 clamp:`Math.Min(Math.Max(val, Minimum), Maximum)` |
| Marquee 不動畫 | Visual Styles 未啟用 | Program.cs 加 `Application.EnableVisualStyles();` |
| 跨執行緒存取例外 | 直接在工作執行緒設 Value | 用 `Invoke` 或 `IProgress` 自動封送 |