你有没有遇到过这样的场景:在开发桌面应用时,需要在DataGridView中显示任务执行进度、文件下载状态、或者数据处理完成度?传统的百分比数字显示方式让用户体验大打折扣,而且很难直观地看出当前状态。
用户更喜欢可视化的进度展示,而不是冰冷的数字。一个直观的进度条不仅能提升用户体验,还能让你的应用看起来更加专业。
本文将手把手教你在WinForm的DataGridView中实现进度条列,让你的数据展示瞬间提升几个档次!
💡 问题分析:为什么需要进度条列?
在日常开发中,我们经常需要在表格中展示以下类型的数据:
传统的文本显示方式存在以下问题:
- 可读性差
- 对比困难
- 用户体验差
🛠️ 解决方案一:使用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(220, 53, 69); // 红色
elseif (progress < 70)
return Color.FromArgb(255, 193, 7); // 黄色
elseif (progress < 100)
return Color.FromArgb(40, 167, 69); // 绿色
else
return Color.FromArgb(23, 162, 184); // 蓝色(完成)
}
}

💡 应用场景说明:
- 适用于需要高度自定义外观的场景
- 可以实现渐变色、动画效果等高级特性
- 性能良好,适合大数据量显示
⚠️ 常见坑点提醒:
- 必须设置e.Handled = true
- 注意边界检查
- 合理使用using语句
🎨 解决方案二:创建自定义进度条列类
通过继承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(240, 240, 240)))
{
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(200, 200, 200)))
{
graphics.DrawRectangle(borderPen, progressRect);
}
}
// 绘制单元格边框
if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)
{
PaintBorder(graphics, clipBounds, cellBounds, cellStyle, borderStyle);
}
}
private Color GetProgressStartColor(int progress)
{
if (progress < 30) return Color.FromArgb(255, 99, 132);
if (progress < 70) return Color.FromArgb(255, 205, 86);
if (progress < 100) return Color.FromArgb(75, 192, 192);
return Color.FromArgb(54, 162, 235);
}
private Color GetProgressEndColor(int progress)
{
if (progress < 30) return Color.FromArgb(255, 159, 164);
if (progress < 70) return Color.FromArgb(255, 226, 139);
if (progress < 100) return Color.FromArgb(129, 210, 210);
return Color.FromArgb(116, 185, 255);
}
}
// 自定义进度条列类
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(64, 64, 64);
dataGridView1.ColumnHeadersDefaultCellStyle.ForeColor = Color.White;
dataGridView1.ColumnHeadersHeight = 35;
// 设置行样式
dataGridView1.DefaultCellStyle.SelectionBackColor = Color.FromArgb(51, 122, 183);
dataGridView1.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(248, 248, 248);
}
// 关键的绘制方法
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(230, 230, 230)))
{
e.Graphics.FillRectangle(backgroundBrush, progressRect);
}
// 绘制进度条边框
using (Pen borderPen = new Pen(Color.FromArgb(180, 180, 180)))
{
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(40, 167, 69); // 绿色-完成
elseif (progress >= 70)
return Color.FromArgb(23, 162, 184); // 蓝色-接近完成
elseif (progress >= 40)
return Color.FromArgb(255, 193, 7); // 黄色-进行中
elseif (progress > 0)
return Color.FromArgb(255, 87, 34); // 橙色-刚开始
else
return Color.FromArgb(108, 117, 125); // 灰色-未开始
}
// 按钮事件处理
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(1, 8); // 随机增加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<int, int> 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中实现进度条列的三大核心方法:
- 🎨 CellPainting事件自绘
- 🔧 自定义列类
- ⚡ 动态更新机制
掌握这些技巧后,你的WinForm应用将拥有更加现代化和专业的界面表现。记住性能优化的关键:合理使用SuspendLayout
和ResumeLayout
,避免频繁的单个单元格刷新。
阅读原文:https://mp.weixin.qq.com/s/wsj3Z-ZvriQxMfhbNojxeA
该文章在 2025/7/12 12:23:20 编辑过