WinForm之ProgressBar

WinForms ProgressBar 控件全面教程

ProgressBar 是 WinForms 中用于可视化展示任务进度的核心控件,支持水平/垂直进度条、样式定制、多线程安全更新等特性。本教程将系统讲解其核心用法与高级技巧,结合实际案例帮助开发者快速掌握。


直接使用

progressBar1.Value = 0;  //清空进度条
progressBar1.Maximum = 100;  // 设置进度条的范围最大值
for (int i = 0; i < 100; i++) 
{
    progressBar1.Value += 1;
    Thread.Sleep(50);
}

多线程

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace WinForm之ProgressBar
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // 初始化进度条
            progressBar1.Minimum = 0;
            progressBar1.Maximum = 100;
            progressBar1.Value = 0;

   
        }

        /// <summary>
        /// 多线程工作
        /// </summary>
        private void DoWork()
        {
            // 模拟工作
            for (int i = 0; i <= 100; i++)
            {
                // 更新进度
                UpdateProgress(i);

                // 模拟工作耗时
                Thread.Sleep(50);
            }

            // 工作完成后可以显示完成消息
            UpdateProgressComplete();
        }

        /// <summary>
        /// 更新进度条
        /// </summary>
        /// <param name="value"></param>
        private void UpdateProgress(int value)
        {
            if (progressBar1.InvokeRequired)
            {
                progressBar1.BeginInvoke(new Action<int>(UpdateProgress), value);
            }
            else
            {
                progressBar1.Value = value;
            }
        }

        /// <summary>
        /// 工作完成
        /// </summary>
        private void UpdateProgressComplete()
        {
            if (progressBar1.InvokeRequired)
            {
                progressBar1.BeginInvoke(new Action(UpdateProgressComplete));
            }
            else
            {
                MessageBox.Show("工作完成!");
            }
        }



        private void button1_Click(object sender, EventArgs e)
        {
            // 启动后台线程
            Thread workerThread = new Thread(DoWork);
            workerThread.Start();
        }
    }
}

一、基础功能详解

1. 基本属性配置

// 初始化进度条
progressBar1.Minimum = 0;      // 最小值(默认0)
progressBar1.Maximum = 100;     // 最大值(默认100)
progressBar1.Value = 0;         // 当前进度
progressBar1.Step = 10;         // 每次调用PerformStep的增量

// 样式设置
progressBar1.Style = ProgressBarStyle.Blocks; // 块状样式(默认)
// 或 progressBar1.Style = ProgressBarStyle.Continuous; // 平滑样式
// 或 progressBar1.Style = ProgressBarStyle.Marquee; // 滚动条样式(不确定时长时使用)

2. 核心方法

  • Increment(int value):按指定值增加进度
  • PerformStep():按 Step 属性值增加进度
  • MarqueeAnimationSpeed:滚动条速度(仅限 Marquee 样式)
// 示例:分步更新进度
private void StartTask() {
    for (int i = 0; i <= 100; i += 10) {
        progressBar1.PerformStep(); // 每次增加10(需提前设置Step=10)
        // 或 progressBar1.Value = i;
        Thread.Sleep(200); // 模拟耗时操作
    }
}

二、高级功能实现

1. 多线程安全更新

问题:跨线程直接操作 UI 控件会抛出异常
解决方案:使用 InvokeBackgroundWorker

// 方法1:使用Control.Invoke
private void UpdateProgress(int value) {
    if (progressBar1.InvokeRequired) {
        progressBar1.Invoke(new Action<int>(UpdateProgress), value);
    } else {
        progressBar1.Value = value;
        lblStatus.Text = $"进度: {value}%";
    }
}

// 方法2:使用BackgroundWorker(推荐)
private void btnStart_Click(object sender, EventArgs e) {
    BackgroundWorker worker = new BackgroundWorker { WorkerReportsProgress = true };
    worker.DoWork += (s, e) => {
        for (int i = 0; i <= 100; i++) {
            Thread.Sleep(50); // 模拟耗时任务
            worker.ReportProgress(i);
        }
    };
    worker.ProgressChanged += (s, e) => {
        progressBar1.Value = e.ProgressPercentage;
        lblStatus.Text = $"处理中... {e.ProgressPercentage}%";
    };
    worker.RunWorkerCompleted += (s, e) => {
        MessageBox.Show("任务完成!");
    };
    worker.RunWorkerAsync();
}

2. 不确定时长任务(Marquee 模式)

// 适用于无法预估完成时间的任务(如文件复制、网络请求)
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.MarqueeAnimationSpeed = 30; // 滚动速度(0-100)

// 当任务完成时切换回正常模式
private void TaskCompleted() {
    progressBar1.Style = ProgressBarStyle.Blocks;
    progressBar1.Value = progressBar1.Maximum; // 显示100%
}

3. 自定义颜色与样式

方法1:通过P/Invoke修改(需引用System.Runtime.InteropServices)

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

private const uint PBM_SETBARCOLOR = 0x0409; // 设置进度条颜色
private const uint PBM_SETBKCOLOR = 0x0201;  // 设置背景色

private void SetProgressBarColor(ProgressBar progressBar, Color barColor, Color bgColor) {
    SendMessage(progressBar.Handle, PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(barColor));
    SendMessage(progressBar.Handle, PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(bgColor));
}

// 使用示例
SetProgressBarColor(progressBar1, Color.FromArgb(65, 184, 131), Color.WhiteSmoke);

方法2:自定义绘制(需继承控件)

public class CustomProgressBar : ProgressBar {
    protected override void OnPaint(PaintEventArgs e) {
        Rectangle rect = ClientRectangle;
        Graphics g = e.Graphics;
        
        // 绘制背景
        using (SolidBrush brush = new SolidBrush(BackColor)) {
            g.FillRectangle(brush, rect);
        }
        
        // 绘制进度
        rect.Inflate(-3, -3); // 边框留白
        if (Value > 0) {
            Rectangle clip = new Rectangle(rect.X, rect.Y, (int)(rect.Width * ((double)Value / Maximum)), rect.Height);
            using (SolidBrush brush = new SolidBrush(Color.FromArgb(65, 184, 131))) {
                g.FillRectangle(brush, clip);
            }
        }
        
        // 绘制文本(可选)
        string text = $"{Value}%";
        using (Font font = new Font("Arial", 8)) {
            SizeF textSize = g.MeasureString(text, font);
            PointF location = new PointF(
                rect.X + (rect.Width - textSize.Width) / 2,
                rect.Y + (rect.Height - textSize.Height) / 2);
            using (SolidBrush brush = new SolidBrush(ForeColor)) {
                g.DrawString(text, font, brush, location);
            }
        }
    }
}

三、实用场景案例

1. 文件复制进度监控

private void CopyFilesWithProgress(string sourceDir, string destDir) {
    string[] files = Directory.GetFiles(sourceDir);
    int totalFiles = files.Length;
    int copiedFiles = 0;
    
    BackgroundWorker worker = new BackgroundWorker { WorkerReportsProgress = true };
    worker.DoWork += (s, e) => {
        foreach (string file in files) {
            string destFile = Path.Combine(destDir, Path.GetFileName(file));
            File.Copy(file, destFile, true);
            copiedFiles++;
            int progress = (int)((double)copiedFiles / totalFiles * 100);
            worker.ReportProgress(progress);
        }
    };
    worker.ProgressChanged += (s, e) => {
        progressBar1.Value = e.ProgressPercentage;
        lblStatus.Text = $"已复制 {copiedFiles}/{totalFiles} 个文件";
    };
    worker.RunWorkerCompleted += (s, e) => {
        MessageBox.Show("文件复制完成!");
    };
    worker.RunWorkerAsync();
}

2. 多步骤任务进度展示

private void ExecuteMultiStepTask() {
    int totalSteps = 3; // 假设有3个步骤
    int currentStep = 0;
    
    BackgroundWorker worker = new BackgroundWorker { WorkerReportsProgress = true };
    worker.DoWork += (s, e) => {
        // 步骤1:初始化
        currentStep++;
        worker.ReportProgress((int)((double)currentStep / totalSteps * 100), "初始化...");
        Thread.Sleep(1000);
        
        // 步骤2:数据处理
        currentStep++;
        worker.ReportProgress((int)((double)currentStep / totalSteps * 100), "数据处理中...");
        Thread.Sleep(2000);
        
        // 步骤3:生成报告
        currentStep++;
        worker.ReportProgress((int)((double)currentStep / totalSteps * 100), "生成报告...");
        Thread.Sleep(1500);
    };
    worker.ProgressChanged += (s, e) => {
        progressBar1.Value = e.ProgressPercentage;
        lblStatus.Text = e.UserState?.ToString() ?? "";
    };
    worker.RunWorkerAsync();
}

3. 动态调整进度条范围

private void DynamicRangeTask() {
    int currentValue = 0;
    int stage = 1;
    
    BackgroundWorker worker = new BackgroundWorker { WorkerReportsProgress = true };
    worker.DoWork += (s, e) => {
        // 第一阶段:0-50
        progressBar1.Invoke(new Action(() => {
            progressBar1.Minimum = 0;
            progressBar1.Maximum = 50;
        }));
        while (currentValue < 50) {
            currentValue += 5;
            worker.ReportProgress(currentValue, $"阶段1: {currentValue}%");
            Thread.Sleep(300);
        }
        
        // 第二阶段:50-100
        stage = 2;
        progressBar1.Invoke(new Action(() => {
            progressBar1.Minimum = 50;
            progressBar1.Maximum = 100;
            currentValue = 50; // 重置当前值
        }));
        while (currentValue < 100) {
            currentValue += 5;
            worker.ReportProgress(currentValue, $"阶段2: {currentValue - 50}%");
            Thread.Sleep(300);
        }
    };
    worker.ProgressChanged += (s, e) => {
        progressBar1.Value = e.ProgressPercentage;
        lblStatus.Text = e.UserState?.ToString() ?? "";
    };
    worker.RunWorkerAsync();
}

四、性能优化建议

  1. 避免频繁更新

    • 进度变化小于5%时可不更新UI(通过阈值判断)
    • 使用 Stopwatch 控制更新频率(如每200ms更新一次)
  2. 资源释放

    • 长时间运行的任务完成后,确保释放 BackgroundWorker 资源
    • 取消任务时调用 worker.CancelAsync()
  3. 异常处理

    worker.DoWork += (s, e) => {
        try {
            // 任务代码
        } catch (Exception ex) {
            e.Result = ex; // 传递异常信息
        }
    };
    worker.RunWorkerCompleted += (s, e) => {
        if (e.Error != null) {
            MessageBox.Show($"任务失败: {e.Error.Message}");
        }
    };
    

五、常见问题解决方案

  1. 进度条不更新

    • 检查是否在UI线程更新控件
    • 确保 Maximum > MinimumValue 在范围内
  2. Marquee 模式不滚动

    • 确认未设置 Value 属性(会覆盖 Marquee 效果)
    • 检查 MarqueeAnimationSpeed 是否为0
  3. 自定义样式失效

    • P/Invoke 方法在64位系统可能需要额外处理
    • 自定义绘制需设置 DoubleBuffered = true 减少闪烁
  4. 跨线程访问异常

    • 始终使用 InvokeBeginInvoke 更新UI
    • 避免在 DoWork 事件中直接操作控件

六、扩展功能

1. 垂直进度条

通过自定义绘制实现:

public class VerticalProgressBar : ProgressBar {
    protected override CreateParams CreateParams {
        get {
            CreateParams cp = base.CreateParams;
            cp.Style |= 0x04; // PBS_VERTICAL 样式
            return cp;
        }
    }
    
    protected override void OnPaint(PaintEventArgs e) {
        // 自定义绘制逻辑(需调整坐标系)
        // 或使用P/Invoke设置垂直样式
    }
}

2. 分段颜色进度条

public class SegmentedProgressBar : ProgressBar {
    private List<Color> segments = new List<Color>();
    
    public void AddSegment(Color color, double percentage) {
        segments.Add(new Segment { Color = color, Percentage = percentage });
    }
    
    protected override void OnPaint(PaintEventArgs e) {
        base.OnPaint(e);
        
        if (segments.Count == 0) return;
        
        Rectangle rect = ClientRectangle;
        rect.Inflate(-3, -3);
        
        double lastEnd = 0;
        foreach (var segment in segments) {
            double start = lastEnd;
            double end = start + segment.Percentage;
            lastEnd = end;
            
            if (end <= 0 || start >= 1) continue;
            
            int startX = rect.X + (int)(rect.Width * Math.Max(0, start));
            int endX = rect.X + (int)(rect.Width * Math.Min(1, end));
            int width = endX - startX;
            
            if (width > 0) {
                using (SolidBrush brush = new SolidBrush(segment.Color)) {
                    e.Graphics.FillRectangle(brush, startX, rect.Y, width, rect.Height);
                }
            }
        }
    }
    
    private class Segment {
        public Color Color { get; set; }
        public double Percentage { get; set; } // 0-1之间的比例
    }
}

总结

ProgressBar 是 WinForms 中提升用户体验的关键控件,通过合理使用其基础功能与高级特性,可以:

  1. 明确展示任务进度:减少用户等待焦虑
  2. 支持多线程安全更新:避免UI冻结
  3. 灵活适配不同场景:从简单进度到复杂多阶段任务
  4. 实现个性化设计:通过颜色、样式定制增强视觉效果

开发者需注意:

  • 始终在UI线程更新控件
  • 合理设计进度反馈粒度
  • 对长时间任务提供取消机制
  • 考虑无障碍访问需求(如屏幕阅读器支持)

掌握 ProgressBar 的高级用法,可以显著提升 WinForms 应用的交互专业度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值