2025-10-31
C#
00

目录

单元格渲染基础详解
基础渲染类继承
进度条单元格渲染
Form4 窗口
复杂条件渲染
高级渲染技巧
图标增强渲染
性能与最佳实践
注意事项
结语

DataGridView作为Windows窗体应用程序中最常用的数据展示控件,其灵活的单元格渲染机制为开发者提供了无限可能。本文将深入剖析DataGridView单元格渲染的核心技术,帮助开发者解锁自定义单元格渲染的艺术。

单元格渲染基础详解

基础渲染类继承

C#
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 { public class 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() { return new 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; } } } // 创建自定义列类型以使用自定义单元格 public class 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))) { throw new InvalidCastException("必须使用CustomTextCell类型的单元格"); } base.CellTemplate = value; } } } }

image.png

进度条单元格渲染

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppDataGrid { public class 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; else if (progressValue < 60) return Color.Orange; else return Color.Green; } } // 进度条列类定义 public class 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))) { throw new InvalidCastException("单元格必须是ProgressBarCell类型"); } base.CellTemplate = value; } } } }

Form4 窗口

C#
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; } } } }

image.png

复杂条件渲染

C#
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; } // 高值显示为绿色 else if (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); } else if (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++; else if (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); } } }

image.png

高级渲染技巧

图标增强渲染

C#
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 ); } } else if (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; } } } }
C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppDataGrid { // 任务项数据模型 public class TaskItem { public string TaskId { get; set; } public string Status { get; set; } public string TaskName { get; set; } public DateTime Deadline { get; set; } public string PersonInCharge { get; set; } public string Progress { get; set; } } }

image.png

性能与最佳实践

  1. 使用双缓冲减少闪烁
  2. 避免在渲染方法中进行复杂计算
  3. 合理控制重绘区域
  4. 使用抗锯齿渲染提升视觉效果

注意事项

  • 自定义渲染可能影响默认行为
  • 复杂渲染可能降低性能
  • 保持代码简洁高效

结语

在本文中,我们深入探讨了DataGridView控件的自定义单元格渲染机制。通过继承DataGridViewTextBoxCell类并重写Paint方法,我们实现了对单元格内容的个性化展示。这种自定义渲染方式为开发者提供了极大的灵活性,使得DataGridView控件能够满足各种复杂的界面需求。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!