LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

WinForm DataGridView添加进度条列:让数据可视化更直观!

admin
2025年7月12日 12:23 本文热度 51

你有没有遇到过这样的场景:在开发桌面应用时,需要在DataGridView中显示任务执行进度、文件下载状态、或者数据处理完成度?传统的百分比数字显示方式让用户体验大打折扣,而且很难直观地看出当前状态。

用户更喜欢可视化的进度展示,而不是冰冷的数字。一个直观的进度条不仅能提升用户体验,还能让你的应用看起来更加专业。

本文将手把手教你在WinForm的DataGridView中实现进度条列,让你的数据展示瞬间提升几个档次!

💡 问题分析:为什么需要进度条列?

在日常开发中,我们经常需要在表格中展示以下类型的数据:

  • 🔄 任务执行进度:批量数据处理、文件上传下载
  • 📊 完成度指标:项目进度、学习进度、销售达成率
  • ⚡ 实时状态:系统资源占用、网络传输状态

传统的文本显示方式存在以下问题:

  1. 可读性差
    用户需要费力理解数字含义
  2. 对比困难
    无法快速比较不同行的进度差异
  3. 用户体验差
    界面单调,缺乏现代感

🛠️ 解决方案一:使用CellPainting事件自绘进度条

这是最灵活的实现方式,完全可控制进度条的外观和行为。

public partial class Form1 : Form
{
    private DataTable dataTable;

    public Form1()
    
{
        InitializeComponent();
        InitializeDataGridView();
    }

    private void InitializeDataGridView()
    
{
        // 创建数据源
        dataTable = new DataTable();
        dataTable.Columns.Add("任务名称", typeof(string));
        dataTable.Columns.Add("进度", typeof(int)); // 存储0-100的进度值
        dataTable.Columns.Add("状态", typeof(string));

        // 添加示例数据
        dataTable.Rows.Add("文件下载"75"进行中");
        dataTable.Rows.Add("数据同步"45"进行中"); 
        dataTable.Rows.Add("报表生成"100"已完成");
        dataTable.Rows.Add("邮件发送"30"进行中");

        // 绑定数据源
        dataGridView1.DataSource = dataTable;

        // 设置列属性
        dataGridView1.Columns["任务名称"].Width = 120;
        dataGridView1.Columns["进度"].Width = 200;
        dataGridView1.Columns["状态"].Width = 80;

        // 关键:绑定CellPainting事件
        dataGridView1.CellPainting += DataGridView1_CellPainting;

        // 优化显示效果
        dataGridView1.RowTemplate.Height = 25;
        dataGridView1.AllowUserToAddRows = false;
    }

    private void DataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    
{
        // 只对"进度"列进行自定义绘制
        if (e.ColumnIndex == 1 && e.RowIndex >= 0// 进度列的索引为1
        {
            // 获取进度值
            if (int.TryParse(e.Value?.ToString(), out int progressValue))
            {
                // 确保进度值在0-100范围内
                progressValue = Math.Max(0, Math.Min(100, progressValue));

                // 绘制背景
                e.PaintBackground(e.CellBounds, true);

                // 计算进度条区域(留出边距)
                Rectangle progressRect = new Rectangle(
                    e.CellBounds.X + 2,
                    e.CellBounds.Y + 2,
                    e.CellBounds.Width - 4,
                    e.CellBounds.Height - 4
                );

                // 绘制进度条背景(灰色)
                using (Brush backgroundBrush = new SolidBrush(Color.LightGray))
                {
                    e.Graphics.FillRectangle(backgroundBrush, progressRect);
                }

                // 计算填充宽度
                int fillWidth = (int)(progressRect.Width * (progressValue / 100.0));

                if (fillWidth > 0)
                {
                    Rectangle fillRect = new Rectangle(
                        progressRect.X,
                        progressRect.Y,
                        fillWidth,
                        progressRect.Height
                    );

                    // 根据进度值选择颜色
                    Color fillColor = GetProgressColor(progressValue);

                    using (Brush fillBrush = new SolidBrush(fillColor))
                    {
                        e.Graphics.FillRectangle(fillBrush, fillRect);    
                    }
                }

                // 绘制文字(进度百分比)
                string text = $"{progressValue}%";
                using (Brush textBrush = new SolidBrush(Color.Black))
                {
                    StringFormat sf = new StringFormat();
                    sf.Alignment = StringAlignment.Center;
                    sf.LineAlignment = StringAlignment.Center;

                    e.Graphics.DrawString(text, e.CellStyle.Font, textBrush, 
                        e.CellBounds, sf);
                }

                // 绘制边框
                using (Pen borderPen = new Pen(Color.Gray))
                {
                    e.Graphics.DrawRectangle(borderPen, progressRect);
                }

                // 标记为已处理,避免默认绘制
                e.Handled = true;
            }
        }
    }

    /// <summary>
    /// 根据进度值返回对应颜色
    /// </summary>
    private Color GetProgressColor(int progress)
    
{
        if (progress < 30)
            return Color.FromArgb(2205369); // 红色
        elseif (progress < 70)
            return Color.FromArgb(2551937);  // 黄色
        elseif (progress < 100)
            return Color.FromArgb(4016769);  // 绿色
        else
            return Color.FromArgb(23162184); // 蓝色(完成)
    }
}

💡 应用场景说明:

  • 适用于需要高度自定义外观的场景
  • 可以实现渐变色、动画效果等高级特性
  • 性能良好,适合大数据量显示

⚠️ 常见坑点提醒:

  1. 必须设置e.Handled = true
    否则会出现重复绘制
  2. 注意边界检查
    避免进度值超出0-100范围
  3. 合理使用using语句
    及时释放GDI+资源

🎨 解决方案二:创建自定义进度条列类

通过继承DataGridViewColumn创建专用的进度条列,代码更加清晰和可复用,其实就是重绘TextBoxCell。

// 自定义进度条单元格类
publicclass DataGridViewProgressCell : DataGridViewTextBoxCell
{
    public override Type ValueType => typeof(int);

    protected override void Paint(Graphics graphics, Rectangle clipBounds, 
        Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, 
        object value, object formattedValue, string errorText, 
        DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle borderStyle, 
        DataGridViewPaintParts paintParts)

    
{
        // 获取进度值
        int progressVal = 0;
        if (value != null && int.TryParse(value.ToString(), out int tempVal))
        {
            progressVal = Math.Max(0, Math.Min(100, tempVal));
        }

        // 绘制单元格背景
        if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)
        {
            using (var backColorBrush = new SolidBrush(cellStyle.BackColor))
            {
                graphics.FillRectangle(backColorBrush, cellBounds);
            }
        }

        // 绘制进度条
        if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground)
        {
            Rectangle progressRect = new Rectangle(
                cellBounds.X + 2
                cellBounds.Y + 2,
                cellBounds.Width - 4
                cellBounds.Height - 4
            );

            // 进度条背景
            using (var progressBackBrush = new SolidBrush(Color.FromArgb(240240240)))
            {
                graphics.FillRectangle(progressBackBrush, progressRect);
            }

            // 进度条填充
            if (progressVal > 0)
            {
                int fillWidth = (int)(progressRect.Width * (progressVal / 100.0));
                Rectangle fillRect = new Rectangle(progressRect.X, progressRect.Y, 
                    fillWidth, progressRect.Height);

                // 使用线性渐变效果
                using (var fillBrush = new LinearGradientBrush(fillRect, 
                    GetProgressStartColor(progressVal), GetProgressEndColor(progressVal), 
                    LinearGradientMode.Horizontal))
                {
                    graphics.FillRectangle(fillBrush, fillRect);
                }
            }

            // 绘制进度文字
            string progressText = $"{progressVal}%";
            using (var textBrush = new SolidBrush(Color.Black))
            {
                StringFormat stringFormat = new StringFormat
                {
                    Alignment = StringAlignment.Center,
                    LineAlignment = StringAlignment.Center
                };
                graphics.DrawString(progressText, cellStyle.Font, textBrush, 
                    cellBounds, stringFormat);
            }

            // 绘制边框
            using (var borderPen = new Pen(Color.FromArgb(200200200)))
            {
                graphics.DrawRectangle(borderPen, progressRect);
            }
        }

        // 绘制单元格边框
        if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)
        {
            PaintBorder(graphics, clipBounds, cellBounds, cellStyle, borderStyle);
        }
    }

    private Color GetProgressStartColor(int progress)
    
{
        if (progress < 30return Color.FromArgb(25599132);
        if (progress < 70return Color.FromArgb(25520586);
        if (progress < 100return Color.FromArgb(75192192);
        return Color.FromArgb(54162235);
    }

    private Color GetProgressEndColor(int progress)
    
{
        if (progress < 30return Color.FromArgb(255159164);
        if (progress < 70return Color.FromArgb(255226139);
        if (progress < 100return Color.FromArgb(129210210);
        return Color.FromArgb(116185255);
    }
}

// 自定义进度条列类
publicclass DataGridViewProgressColumn : DataGridViewColumn
{
    public DataGridViewProgressColumn() : base(new DataGridViewProgressCell())
    
{

    }

    public override DataGridViewCell CellTemplate
    {
        get => base.CellTemplate;
        set
        {
            if (value != null && !value.GetType().IsAssignableFrom(typeof(DataGridViewProgressCell)))
            {
                thrownew InvalidCastException("必须是DataGridViewProgressCell类型");
            }
            base.CellTemplate = value;
        }
    }
}

使用自定义进度条列:

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;

namespace AppDataGridBar
{
    public partial class Form2 : Form
    {
        public Form2()
        
{
            InitializeComponent();
            InitializeCustomProgressColumn();
        }

        private void InitializeCustomProgressColumn()
        
{
            // 清除自动生成的列
            dataGridView1.AutoGenerateColumns = false;
            dataGridView1.Columns.Clear();

            // 添加普通列 - 关键是要设置DataPropertyName
            DataGridViewTextBoxColumn taskNameColumn = new DataGridViewTextBoxColumn
            {
                Name = "TaskName",
                HeaderText = "任务名称",
                DataPropertyName = "TaskName"// 绑定到TaskInfo.TaskName属性
            };
            dataGridView1.Columns.Add(taskNameColumn);

            // 如果要添加进度条列,取消注释这部分
            DataGridViewProgressColumn progressColumn = new DataGridViewProgressColumn
            {
                Name = "Progress",
                HeaderText = "完成进度",
                Width = 200,
                DataPropertyName = "Progress"
            };
            dataGridView1.Columns.Add(progressColumn);

            // 添加状态列
            DataGridViewTextBoxColumn statusColumn = new DataGridViewTextBoxColumn
            {
                Name = "Status",
                HeaderText = "状态",
                DataPropertyName = "Status"// 绑定到TaskInfo.Status属性
            };
            dataGridView1.Columns.Add(statusColumn);

            // 绑定数据源
            List<TaskInfo> tasks = new List<TaskInfo>
            {
                new TaskInfo { TaskName = "系统备份", Progress = 85, Status = "进行中" },
                new TaskInfo { TaskName = "数据清理", Progress = 60, Status = "进行中" },
                new TaskInfo { TaskName = "日志归档", Progress = 100, Status = "已完成" }
            };

            dataGridView1.DataSource = tasks;
        }

        // 数据模型类
        publicclass TaskInfo
        {

            publicstring TaskName { get; set; }
            publicint Progress { get; set; }
            publicstring Status { get; set; }
        }
    }
}

⚡ 解决方案三:动态更新进度条

实际应用中,进度条需要实时更新。以下展示如何实现动态进度更新:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;

namespace AppDataGridBar
{
    public partial class Form3 : Form
    {
        private DataTable dataTable;
        private Timer updateTimer;
        private Random random = new Random();

        public Form3()
        
{
            InitializeComponent();
            InitializeDataGridView();
        }

        private void InitializeDataGridView()
        
{
            // 创建数据源
            dataTable = new DataTable();
            dataTable.Columns.Add("任务名称", typeof(string));
            dataTable.Columns.Add("进度", typeof(int)); // 存储0-100的进度值
            dataTable.Columns.Add("状态", typeof(string));

            // 添加示例数据
            dataTable.Rows.Add("文件下载"75"进行中");
            dataTable.Rows.Add("数据同步"45"进行中");
            dataTable.Rows.Add("报表生成"100"已完成");
            dataTable.Rows.Add("邮件发送"30"进行中");
            dataTable.Rows.Add("备份数据"0"待开始");

            // 绑定数据源
            dataGridView1.DataSource = dataTable;

            // 设置列属性
            dataGridView1.Columns["任务名称"].Width = 120;
            dataGridView1.Columns["进度"].Width = 250// 增加宽度以容纳进度条
            dataGridView1.Columns["状态"].Width = 80;

            // 关键:绑定CellPainting事件
            dataGridView1.CellPainting += DataGridView1_CellPainting;

            // 优化显示效果
            dataGridView1.RowTemplate.Height = 30;
            dataGridView1.AllowUserToAddRows = false;
            dataGridView1.AllowUserToDeleteRows = false;
            dataGridView1.ReadOnly = true;
            dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;

            // 设置表格样式
            dataGridView1.EnableHeadersVisualStyles = false;
            dataGridView1.ColumnHeadersDefaultCellStyle.BackColor = Color.FromArgb(646464);
            dataGridView1.ColumnHeadersDefaultCellStyle.ForeColor = Color.White;
            dataGridView1.ColumnHeadersHeight = 35;

            // 设置行样式
            dataGridView1.DefaultCellStyle.SelectionBackColor = Color.FromArgb(51122183);
            dataGridView1.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(248248248);
        }

        // 关键的绘制方法
        private void DataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
        
{
            // 只处理进度列(第1列,索引为1)
            if (e.ColumnIndex == 1 && e.RowIndex >= 0)
            {
                // 获取进度值
                int progress = Convert.ToInt32(dataGridView1.Rows[e.RowIndex].Cells["进度"].Value);

                // 绘制背景
                e.PaintBackground(e.CellBounds, true);

                // 计算进度条区域
                Rectangle progressRect = e.CellBounds;
                progressRect.Inflate(-3-3); // 留出边距

                // 绘制进度条背景
                using (SolidBrush backgroundBrush = new SolidBrush(Color.FromArgb(230230230)))
                {
                    e.Graphics.FillRectangle(backgroundBrush, progressRect);
                }

                // 绘制进度条边框
                using (Pen borderPen = new Pen(Color.FromArgb(180180180)))
                {
                    e.Graphics.DrawRectangle(borderPen, progressRect);
                }

                // 计算进度条填充区域
                int fillWidth = (int)(progressRect.Width * progress / 100.0);
                Rectangle fillRect = new Rectangle(progressRect.X, progressRect.Y, fillWidth, progressRect.Height);

                // 根据进度选择颜色
                Color progressColor = GetProgressColor(progress);

                // 绘制进度填充
                if (fillWidth > 0)
                {
                    using (LinearGradientBrush fillBrush = new LinearGradientBrush(
                        fillRect,
                        Color.FromArgb(255, progressColor.R, progressColor.G, progressColor.B),
                        Color.FromArgb(200, progressColor.R, progressColor.G, progressColor.B),
                        LinearGradientMode.Vertical))
                    {
                        e.Graphics.FillRectangle(fillBrush, fillRect);
                    }
                }

                // 绘制进度文本
                string progressText = $"{progress}%";
                using (Font font = new Font("Microsoft YaHei"9, FontStyle.Bold))
                {
                    SizeF textSize = e.Graphics.MeasureString(progressText, font);
                    PointF textPoint = new PointF(
                        progressRect.X + (progressRect.Width - textSize.Width) / 2,
                        progressRect.Y + (progressRect.Height - textSize.Height) / 2
                    );

                    // 根据背景选择文字颜色
                    Color textColor = progress > 50 ? Color.White : Color.Black;
                    using (SolidBrush textBrush = new SolidBrush(textColor))
                    {
                        e.Graphics.DrawString(progressText, font, textBrush, textPoint);
                    }
                }

                e.Handled = true;
            }
        }

        // 根据进度值返回对应的颜色
        private Color GetProgressColor(int progress)
        
{
            if (progress == 100)
                return Color.FromArgb(4016769); // 绿色-完成
            elseif (progress >= 70)
                return Color.FromArgb(23162184); // 蓝色-接近完成
            elseif (progress >= 40)
                return Color.FromArgb(2551937); // 黄色-进行中
            elseif (progress > 0)
                return Color.FromArgb(2558734); // 橙色-刚开始
            else
                return Color.FromArgb(108117125); // 灰色-未开始
        }



        // 按钮事件处理
        private void BtnStart_Click(object sender, EventArgs e)
        
{
            StartProgressSimulation();
        }

        private void BtnStop_Click(object sender, EventArgs e)
        
{
            StopProgressSimulation();
        }

        private void BtnReset_Click(object sender, EventArgs e)
        
{
            ResetProgress();
        }

        private void BtnAddTask_Click(object sender, EventArgs e)
        
{
            AddNewTask();
        }

        private void StartProgressSimulation()
        
{
            if (updateTimer == null)
            {
                // 创建定时器,每200毫秒更新一次进度
                updateTimer = new Timer();
                updateTimer.Interval = 200;
                updateTimer.Tick += UpdateTimer_Tick;
            }

            if (!updateTimer.Enabled)
            {
                updateTimer.Start();
            }
        }

        private void StopProgressSimulation()
        
{
            if (updateTimer != null && updateTimer.Enabled)
            {
                updateTimer.Stop();
            }
        }

        private void ResetProgress()
        
{
            StopProgressSimulation();

            // 重置所有进度
            foreach (DataRow row in dataTable.Rows)
            {
                row["进度"] = 0;
                row["状态"] = "待开始";
            }

            dataGridView1.Invalidate();
        }

        private void AddNewTask()
        
{
            string taskName = $"新任务{dataTable.Rows.Count + 1}";
            dataTable.Rows.Add(taskName, 0"待开始");
        }

        private void UpdateTimer_Tick(object sender, EventArgs e)
        
{
            // 模拟进度更新
            bool hasUpdates = false;

            foreach (DataRow row in dataTable.Rows)
            {
                int currentProgress = Convert.ToInt32(row["进度"]);

                // 如果未完成,随机增加进度
                if (currentProgress < 100)
                {
                    int increment = random.Next(18); // 随机增加1-7
                    int newProgress = Math.Min(100, currentProgress + increment);
                    row["进度"] = newProgress;
                    hasUpdates = true;

                    // 更新状态
                    if (newProgress == 100)
                    {
                        row["状态"] = "已完成";
                    }
                    elseif (newProgress > 0)
                    {
                        row["状态"] = "进行中";
                    }
                }
            }

            // 只在有更新时刷新
            if (hasUpdates)
            {
                dataGridView1.InvalidateColumn(1); // 只刷新进度列
                dataGridView1.InvalidateColumn(2); // 刷新状态列
            }

            // 检查是否所有任务都完成
            bool allCompleted = true;
            foreach (DataRow row in dataTable.Rows)
            {
                if (Convert.ToInt32(row["进度"]) < 100)
                {
                    allCompleted = false;
                    break;
                }
            }

            if (allCompleted)
            {
                updateTimer.Stop();
                MessageBox.Show("所有任务已完成!""提示", MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
            }
        }

        // 手动更新特定行的进度
        public void UpdateProgress(int rowIndex, int newProgress)
        
{
            if (rowIndex >= 0 && rowIndex < dataTable.Rows.Count)
            {
                int clampedProgress = Math.Max(0, Math.Min(100, newProgress));
                dataTable.Rows[rowIndex]["进度"] = clampedProgress;

                // 更新状态
                if (clampedProgress == 100)
                    dataTable.Rows[rowIndex]["状态"] = "已完成";
                elseif (clampedProgress > 0)
                    dataTable.Rows[rowIndex]["状态"] = "进行中";
                else
                    dataTable.Rows[rowIndex]["状态"] = "待开始";

                // 只刷新指定行
                dataGridView1.InvalidateRow(rowIndex);
            }
        }

        // 批量更新进度
        public void UpdateMultipleProgress(Dictionary<intint> progressUpdates)
        
{
            foreach (var update in progressUpdates)
            {
                UpdateProgress(update.Key, update.Value);
            }
        }

        // 获取所有任务的进度信息
        public List<TaskProgress> GetAllProgress()
        {
            List<TaskProgress> progressList = new List<TaskProgress>();

            for (int i = 0; i < dataTable.Rows.Count; i++)
            {
                DataRow row = dataTable.Rows[i];
                progressList.Add(new TaskProgress
                {
                    TaskName = row["任务名称"].ToString(),
                    Progress = Convert.ToInt32(row["进度"]),
                    Status = row["状态"].ToString(),
                    RowIndex = i
                });
            }

            return progressList;
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        
{
            StopProgressSimulation();
            base.OnFormClosing(e);
        }
    }

    // 任务进度信息类
    publicclass TaskProgress
    {

        publicstring TaskName { get; set; }
        publicint Progress { get; set; }
        publicstring Status { get; set; }
        publicint RowIndex { get; set; }
    }
}

💬 互动交流

你在项目中还遇到过哪些DataGridView的显示需求? 比如图表列、图片列、或者其他自定义控件列?

有没有在进度条显示上遇到性能问题? 特别是大数据量情况下的优化经验,欢迎在评论区分享!

🎉 总结要点

通过本文的学习,你已经掌握了在WinForm DataGridView中实现进度条列的三大核心方法

  1. 🎨 CellPainting事件自绘
    灵活度最高,适合复杂定制需求
  2. 🔧 自定义列类
    代码清晰,可复用性强,推荐用于产品级开发
  3. ⚡ 动态更新机制
    实现实时进度显示,提升用户体验

掌握这些技巧后,你的WinForm应用将拥有更加现代化和专业的界面表现。记住性能优化的关键:合理使用SuspendLayoutResumeLayout,避免频繁的单个单元格刷新。


阅读原文:https://mp.weixin.qq.com/s/wsj3Z-ZvriQxMfhbNojxeA


该文章在 2025/7/12 12:23:20 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved