DataGridView作为Windows窗体应用程序中最常用的数据展示控件,其灵活的单元格渲染机制为开发者提供了无限可能。本文将深入剖析DataGridView单元格渲染的核心技术,帮助开发者解锁自定义单元格渲染的艺术。
单元格渲染基础详解
基础渲染类继承
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AppDataGrid
{
publicclass CustomTextCell : DataGridViewTextBoxCell
{
protected override void Paint(
Graphics graphics,
Rectangle clipBounds,
Rectangle cellBounds,
int rowIndex,
DataGridViewElementStates cellState,
object value,
object formattedValue,
string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
// 修改:我们需要绘制除了文本之外的所有部分(背景、边框等)
// 通过排除文本部分,避免基类绘制文本造成重叠
DataGridViewPaintParts partsWithoutText = paintParts & ~DataGridViewPaintParts.ContentForeground;
// 调用基类渲染方法,但不包括文本部分
base.Paint(graphics, clipBounds, cellBounds, rowIndex,
cellState, value, formattedValue,
errorText, cellStyle, advancedBorderStyle, partsWithoutText);
// 只有当需要绘制内容前景(文本)时才进行自定义文本绘制
if ((paintParts & DataGridViewPaintParts.ContentForeground) != 0 && value != null)
{
// 抗锯齿绘制
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
// 自定义文本样式
using (SolidBrush brush = new SolidBrush(Color.Navy))
using (Font customFont = new Font("微软雅黑", 9, FontStyle.Bold))
{
// 创建适合文本对齐的StringFormat
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
// 调整文本绘制区域,留出边距
Rectangle textRect = new Rectangle(
cellBounds.X + 2,
cellBounds.Y + 2,
cellBounds.Width - 4,
cellBounds.Height - 4);
graphics.DrawString(
value.ToString(),
customFont,
brush,
textRect,
stringFormat
);
}
}
}
// 重写Clone方法以确保正确复制单元格
public override object Clone()
{
returnnew CustomTextCell();
}
// 确保默认新行单元格值正确
public override object DefaultNewRowValue => string.Empty;
// ====== 修复编辑功能 ======
// 使用标准的TextBox作为编辑控件
public override Type EditType => typeof(DataGridViewTextBoxEditingControl);
// 设置值类型
public override Type ValueType => typeof(string);
// 正确处理准备编辑状态
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
// 调用基类方法确保正确初始化编辑控件
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
// 获取并配置编辑控件
if (DataGridView.EditingControl is DataGridViewTextBoxEditingControl textBox)
{
// 如果单元格值不为空,则设置文本框内容
textBox.Text = (Value == null) ? string.Empty : Value.ToString();
// 可选:配置文本框的其他属性,比如字体等
textBox.Font = new Font("微软雅黑", 9, FontStyle.Regular);
}
}
// 确保可以编辑
public override bool ReadOnly
{
get { return base.ReadOnly; }
set { base.ReadOnly = value; }
}
}
// 创建自定义列类型以使用自定义单元格
publicclass CustomTextColumn : DataGridViewColumn
{
public CustomTextColumn() : base(new CustomTextCell())
{
// 设置列的默认属性
SortMode = DataGridViewColumnSortMode.Automatic;
DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
public override DataGridViewCell CellTemplate
{
get { return base.CellTemplate; }
set
{
// 确保始终使用自定义单元格类型
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(CustomTextCell)))
{
thrownew InvalidCastException("必须使用CustomTextCell类型的单元格");
}
base.CellTemplate = value;
}
}
}
}

进度条单元格渲染
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AppDataGrid
{
publicclass ProgressBarCell : DataGridViewTextBoxCell
{
protected override void Paint(
Graphics graphics,
Rectangle clipBounds,
Rectangle cellBounds,
int rowIndex,
DataGridViewElementStates cellState,
object value,
object formattedValue,
string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
// 解析进度值
int progressValue = 0;
if (value != null && int.TryParse(value.ToString(), out progressValue))
{
progressValue = Math.Max(0, Math.Min(100, progressValue));
// 绘制背景
using (SolidBrush backgroundBrush = new SolidBrush(Color.LightGray))
{
graphics.FillRectangle(backgroundBrush, cellBounds);
}
// 绘制进度条
using (SolidBrush progressBrush = new SolidBrush(GetProgressColor(progressValue)))
{
int width = (int)((progressValue / 100.0) * cellBounds.Width);
Rectangle progressRect = new Rectangle(
cellBounds.X,
cellBounds.Y,
width,
cellBounds.Height
);
graphics.FillRectangle(progressBrush, progressRect);
}
// 绘制文本
using (SolidBrush textBrush = new SolidBrush(Color.Black))
using (StringFormat sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
graphics.DrawString(
$"{progressValue}%",
cellStyle.Font,
textBrush,
cellBounds,
sf
);
}
}
else
{
// 如果值不是有效的整数,则调用基类绘制方法
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState,
value, formattedValue, errorText, cellStyle,
advancedBorderStyle, paintParts);
}
}
// 根据进度值选择颜色
private Color GetProgressColor(int progressValue)
{
if (progressValue < 30)
return Color.Red;
elseif (progressValue < 60)
return Color.Orange;
else
return Color.Green;
}
}
// 进度条列类定义
publicclass ProgressBarColumn : DataGridViewColumn
{
public ProgressBarColumn() : base(new ProgressBarCell())
{
}
public override DataGridViewCell CellTemplate
{
get { return base.CellTemplate; }
set
{
// 确保使用的单元格类型为ProgressBarCell
if (value != null && !value.GetType().IsAssignableFrom(typeof(ProgressBarCell)))
{
thrownew InvalidCastException("单元格必须是ProgressBarCell类型");
}
base.CellTemplate = value;
}
}
}
}
Form4 窗口
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 AppDataGrid
{
public partial class Form4 : Form
{
private Random random = new Random();
public Form4()
{
InitializeComponent();
dataGridView1.AllowUserToAddRows = false;
dataGridView1.AutoGenerateColumns = false;
InitializeDataGridView();
}
private void InitializeDataGridView()
{
// 添加普通文本列
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
{
HeaderText = "任务名称",
Name = "TaskName",
Width = 200
});
// 添加进度条列
dataGridView1.Columns.Add(new ProgressBarColumn
{
HeaderText = "完成进度",
Name = "Progress",
Width = 150
});
// 添加一些示例数据
dataGridView1.Rows.Add("任务 1", 25);
dataGridView1.Rows.Add("任务 2", 50);
dataGridView1.Rows.Add("任务 3", 75);
dataGridView1.Rows.Add("任务 4", 100);
}
private void btnUpdateProgress_Click(object sender, EventArgs e)
{
// 随机更新进度
foreach (DataGridViewRow row in dataGridView1.Rows)
{
int progress = random.Next(0, 101);
row.Cells["Progress"].Value = progress;
}
}
}
}

复杂条件渲染
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 AppDataGrid
{
public partial class Form5 : Form
{
private DataGridView dataGridView1;
public Form5()
{
InitializeComponent();
// 初始化界面
InitializeUI();
// 加载数据
LoadData();
// 配置条件格式化
ConfigureConditionalRendering();
}
private void InitializeUI()
{
// 设置窗体属性
this.Text = "数据条件格式化示例";
this.Size = new Size(800, 500);
// 初始化 DataGridView
dataGridView1 = new DataGridView();
dataGridView1.Dock = DockStyle.Fill;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.ReadOnly = true;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
// 添加到窗体
this.Controls.Add(dataGridView1);
}
private void LoadData()
{
// 创建示例数据表
DataTable dataTable = new DataTable();
// 添加列
dataTable.Columns.Add("项目名称", typeof(string));
dataTable.Columns.Add("日期", typeof(DateTime));
dataTable.Columns.Add("金额", typeof(decimal));
dataTable.Columns.Add("状态", typeof(string));
// 添加示例数据行
dataTable.Rows.Add("销售收入", DateTime.Now.AddDays(-5), 1500.50m, "已完成");
dataTable.Rows.Add("设备购置", DateTime.Now.AddDays(-3), -2350.75m, "已支付");
dataTable.Rows.Add("服务费用", DateTime.Now.AddDays(-2), 850.25m, "处理中");
dataTable.Rows.Add("广告费用", DateTime.Now.AddDays(-1), -450.00m, "已支付");
dataTable.Rows.Add("咨询收入", DateTime.Now, 2200.00m, "已完成");
dataTable.Rows.Add("办公用品", DateTime.Now.AddDays(-4), -120.50m, "已支付");
dataTable.Rows.Add("销售佣金", DateTime.Now.AddDays(-2), 950.75m, "处理中");
dataTable.Rows.Add("年度奖金", DateTime.Now.AddDays(-1), 5000.00m, "已完成");
// 将数据绑定到 DataGridView
dataGridView1.DataSource = dataTable;
// 设置列格式
dataGridView1.Columns["日期"].DefaultCellStyle.Format = "yyyy-MM-dd";
dataGridView1.Columns["金额"].DefaultCellStyle.Format = "N2"; // 两位小数
// 调整列宽度
dataGridView1.Columns["项目名称"].FillWeight = 25;
dataGridView1.Columns["日期"].FillWeight = 25;
dataGridView1.Columns["金额"].FillWeight = 25;
dataGridView1.Columns["状态"].FillWeight = 25;
}
private void ConfigureConditionalRendering()
{
dataGridView1.CellFormatting += (sender, e) =>
{
// 根据单元格值设置样式
if (e.ColumnIndex == 2) // 金额列
{
if (e.Value != null && e.Value != DBNull.Value)
{
decimal value = Convert.ToDecimal(e.Value);
// 负值显示为红色
if (value < 0)
{
e.CellStyle.ForeColor = Color.Red;
e.CellStyle.BackColor = Color.LightPink;
// 为负值添加括号显示
e.Value = string.Format("({0:N2})", Math.Abs(value));
e.FormattingApplied = true;
}
// 高值显示为绿色
elseif (value > 1000)
{
e.CellStyle.ForeColor = Color.Green;
e.CellStyle.BackColor = Color.LightGreen;
}
}
}
// 根据状态列设置行样式
if (e.ColumnIndex == 3) // 状态列
{
string status = e.Value?.ToString();
if (status == "已完成")
{
e.CellStyle.Font = new Font(dataGridView1.Font, FontStyle.Bold);
}
elseif (status == "处理中")
{
e.CellStyle.ForeColor = Color.Blue;
}
}
};
// 添加行选择事件
dataGridView1.SelectionChanged += (sender, e) =>
{
if (dataGridView1.SelectedRows.Count > 0)
{
// 在这里可以添加选择行后的操作
// 例如:显示详细信息、更新统计等
}
};
// 添加摘要信息
AddSummaryPanel();
}
private void AddSummaryPanel()
{
// 创建摘要面板
Panel summaryPanel = new Panel();
summaryPanel.Dock = DockStyle.Bottom;
summaryPanel.Height = 60;
summaryPanel.BackColor = Color.LightGray;
// 添加统计标签
Label summaryLabel = new Label();
summaryLabel.AutoSize = false;
summaryLabel.Dock = DockStyle.Fill;
summaryLabel.TextAlign = ContentAlignment.MiddleCenter;
// 计算统计信息
decimal total = 0;
int positiveCount = 0;
int negativeCount = 0;
DataTable dt = (DataTable)dataGridView1.DataSource;
foreach (DataRow row in dt.Rows)
{
decimal amount = Convert.ToDecimal(row["金额"]);
total += amount;
if (amount > 0)
positiveCount++;
elseif (amount < 0)
negativeCount++;
}
// 设置统计文本
summaryLabel.Text = string.Format(
"总计: {0:C2} | 收入项: {1} | 支出项: {2} | 总项数: {3}",
total, positiveCount, negativeCount, dt.Rows.Count);
// 将标签添加到面板
summaryPanel.Controls.Add(summaryLabel);
// 调整DataGridView位置
dataGridView1.Dock = DockStyle.Fill;
// 将面板添加到窗体
this.Controls.Add(summaryPanel);
}
}
}

高级渲染技巧
图标增强渲染
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 AppDataGrid
{
public partial class Form6 : Form
{
DataGridView dataGridView1;
public Form6()
{
InitializeComponent();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
dataGridView1.Dock = DockStyle.Fill;
this.Controls.Add(dataGridView1);
dataGridView1.AutoGenerateColumns = false;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.RowHeadersVisible = false;
dataGridView1.AllowUserToResizeRows = false;
dataGridView1.MultiSelect = false;
dataGridView1.BackgroundColor = Color.White;
dataGridView1.BorderStyle = BorderStyle.Fixed3D;
dataGridView1.RowTemplate.Height = 28;
// 添加列
var taskIdColumn = new DataGridViewTextBoxColumn
{
Name = "TaskId",
HeaderText = "任务编号",
DataPropertyName = "TaskId",
Width = 100
};
var statusColumn = new DataGridViewTextBoxColumn
{
Name = "Status",
HeaderText = "状态",
DataPropertyName = "Status",
Width = 120
};
var taskNameColumn = new DataGridViewTextBoxColumn
{
Name = "TaskName",
HeaderText = "任务名称",
DataPropertyName = "TaskName",
Width = 200
};
var deadlineColumn = new DataGridViewTextBoxColumn
{
Name = "Deadline",
HeaderText = "截止日期",
DataPropertyName = "Deadline",
Width = 140
};
var personInChargeColumn = new DataGridViewTextBoxColumn
{
Name = "PersonInCharge",
HeaderText = "负责人",
DataPropertyName = "PersonInCharge",
Width = 120
};
var progressColumn = new DataGridViewTextBoxColumn
{
Name = "Progress",
HeaderText = "进度",
DataPropertyName = "Progress",
Width = 80
};
// 添加列到DataGridView
dataGridView1.Columns.AddRange(new DataGridViewColumn[]
{
taskIdColumn, statusColumn, taskNameColumn, deadlineColumn, personInChargeColumn, progressColumn
});
LoadSampleData();
dataGridView1.CellPainting += dataGridView1_CellPainting;
}
private void LoadSampleData()
{
// 创建示例数据
var tasks = new List<TaskItem>
{
new TaskItem { TaskId = "T001", Status = "已完成", TaskName = "需求分析报告", Deadline = DateTime.Now.AddDays(-5), PersonInCharge = "张三", Progress = "100%" },
new TaskItem { TaskId = "T002", Status = "进行中", TaskName = "系统架构设计", Deadline = DateTime.Now.AddDays(3), PersonInCharge = "李四", Progress = "60%" },
new TaskItem { TaskId = "T003", Status = "进行中", TaskName = "数据库设计", Deadline = DateTime.Now.AddDays(2), PersonInCharge = "王五", Progress = "45%" },
new TaskItem { TaskId = "T004", Status = "未开始", TaskName = "前端界面开发", Deadline = DateTime.Now.AddDays(10), PersonInCharge = "赵六", Progress = "0%" },
new TaskItem { TaskId = "T005", Status = "已完成", TaskName = "项目计划书", Deadline = DateTime.Now.AddDays(-10), PersonInCharge = "张三", Progress = "100%" },
new TaskItem { TaskId = "T006", Status = "未开始", TaskName = "接口文档编写", Deadline = DateTime.Now.AddDays(7), PersonInCharge = "李四", Progress = "0%" },
new TaskItem { TaskId = "T007", Status = "进行中", TaskName = "后端API开发", Deadline = DateTime.Now.AddDays(5), PersonInCharge = "王五", Progress = "30%" }
};
// 绑定数据源
dataGridView1.DataSource = tasks;
}
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
// 只处理数据单元格,忽略标题行/列
if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
{
// 如果是"状态"列
if (dataGridView1.Columns[e.ColumnIndex].Name == "Status")
{
RenderCellWithIcon(e);
}
}
}
private void RenderCellWithIcon(DataGridViewCellPaintingEventArgs e)
{
// 清除单元格默认内容
e.PaintBackground(e.CellBounds, true);
// 获取状态图标
Image statusIcon = GetStatusIcon(e.Value);
if (statusIcon != null)
{
// 计算图标位置(左侧)
Rectangle iconRect = new Rectangle(
e.CellBounds.X + 4,
e.CellBounds.Y + (e.CellBounds.Height - 16) / 2,
16,
16
);
// 绘制图标
e.Graphics.DrawImage(statusIcon, iconRect);
// 计算文本位置(图标右侧)
Rectangle textRect = new Rectangle(
iconRect.Right + 4,
e.CellBounds.Y,
e.CellBounds.Width - iconRect.Width - 8,
e.CellBounds.Height
);
// 设置文本格式
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Center;
// 绘制文本
using (Brush textBrush = new SolidBrush(e.CellStyle.ForeColor))
{
e.Graphics.DrawString(
e.Value?.ToString(),
e.CellStyle.Font,
textBrush,
textRect,
sf
);
}
}
elseif (e.Value != null)
{
// 如果没有图标但有值,只绘制文本
Rectangle textRect = new Rectangle(
e.CellBounds.X + 4,
e.CellBounds.Y,
e.CellBounds.Width - 8,
e.CellBounds.Height
);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Center;
using (Brush textBrush = new SolidBrush(e.CellStyle.ForeColor))
{
e.Graphics.DrawString(
e.Value.ToString(),
e.CellStyle.Font,
textBrush,
textRect,
sf
);
}
}
e.Handled = true;
}
private Image GetStatusIcon(object value)
{
// 根据状态文本返回对应图标
switch (value?.ToString())
{
case"已完成":
return Image.FromFile("./images/complete.png");
case"进行中":
return Image.FromFile("./images/processing.png");
case"未开始":
return Image.FromFile("./images/pending.png");
default:
return null;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AppDataGrid
{
// 任务项数据模型
publicclass TaskItem
{
publicstring TaskId { get; set; }
publicstring Status { get; set; }
publicstring TaskName { get; set; }
public DateTime Deadline { get; set; }
publicstring PersonInCharge { get; set; }
publicstring Progress { get; set; }
}
}

性能与最佳实践
- 使用双缓冲减少闪烁
- 避免在渲染方法中进行复杂计算
- 合理控制重绘区域
- 使用抗锯齿渲染提升视觉效果
注意事项
- 自定义渲染可能影响默认行为
- 复杂渲染可能降低性能
- 保持代码简洁高效
结语
在本文中,我们深入探讨了DataGridView控件的自定义单元格渲染机制。通过继承DataGridViewTextBoxCell类并重写Paint方法,我们实现了对单元格内容的个性化展示。这种自定义渲染方式为开发者提供了极大的灵活性,使得DataGridView控件能够满足各种复杂的界面需求。
该文章在 2025/6/10 12:06:45 编辑过