如何在C#的DataGridView单元格中同时添加文本与图像?
实现带行列号表头及图文混合单元格的DataGridView
针对你的需求,不需要拆分行来实现图文显示,通过自定义DataGridView单元格和表头自定义绘制就能完美解决,既保持原有行列数量,又能实现所有要求的功能。
一、核心思路
- 表头行列号:通过自定义列头单元格绘制列号,利用行的
RowPostPaint事件绘制行号,不修改原有表头结构。 - 图文混合单元格:自定义
DataGridViewCell,重写Paint方法手动绘制文本(上方)和图像(下方),完全控制布局,无需调整行列数量。
二、具体实现代码(C# WinForms)
1. 自定义图文单元格与列
// 自定义支持文本+图像的单元格,文本在图像上方 public class ImageTextCell : 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) { // 先绘制单元格基础样式(背景、边框等),跳过默认文本绘制 base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts & ~DataGridViewPaintParts.ContentForeground); // 解析单元格数据:假设数据是文本+图像的键值对(也可替换为自定义实体类) if (value is KeyValuePair<string, Image> data) { string text = data.Key; Image image = data.Value; // 绘制文本:居中,顶部留5px间距 SizeF textSize = graphics.MeasureString(text, cellStyle.Font); PointF textPos = new PointF( cellBounds.Left + (cellBounds.Width - textSize.Width) / 2, cellBounds.Top + 5 ); using (var textBrush = new SolidBrush(cellStyle.ForeColor)) { graphics.DrawString(text, cellStyle.Font, textBrush, textPos); } // 绘制图像:居中,在文本下方留5px间距 if (image != null) { int imageTop = (int)(textPos.Y + textSize.Height + 5); int imageLeft = cellBounds.Left + (cellBounds.Width - image.Width) / 2; Rectangle imageRect = new Rectangle(imageLeft, imageTop, image.Width, image.Height); // 确保图像不超出单元格范围 imageRect.Intersect(cellBounds); graphics.DrawImage(image, imageRect); } } } } // 自定义列,绑定上面的图文单元格 public class ImageTextColumn : DataGridViewColumn { public ImageTextColumn() : base(new ImageTextCell()) { Width = 120; // 设置默认列宽,适配图文显示 } public override DataGridViewCell CellTemplate { get => base.CellTemplate; set { if (value != null && !(value is ImageTextCell)) throw new InvalidCastException("必须使用ImageTextCell作为单元格模板"); base.CellTemplate = value; } } }
2. 实现表头行列号与DataGridView配置
public partial class MainForm : Form { public MainForm() { InitializeComponent(); ConfigureDataGridView(); LoadTestData(); } private void ConfigureDataGridView() { var grid = dataGridView1; grid.RowHeadersWidth = 60; // 扩大行头宽度,容纳行号 grid.ColumnHeadersHeight = 40; // 扩大列头高度,同时显示列号和列名 grid.RowTemplate.Height = 100; // 设置行高,足够显示图文 // 添加自定义图文列 for (int i = 0; i < 3; i++) { var col = new ImageTextColumn(); col.Name = $"Column{i+1}"; col.HeaderText = $"分类{i+1}"; grid.Columns.Add(col); } // 绑定行号绘制事件 grid.RowPostPaint += Grid_RowPostPaint; // 设置自定义列头单元格,显示列号 grid.ColumnHeaderCellTemplate = new NumberedColumnHeaderCell(); } // 绘制行号(行索引+1) private void Grid_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) { var grid = sender as DataGridView; string rowNum = (e.RowIndex + 1).ToString(); var font = new Font(grid.Font.FontFamily, 10, FontStyle.Bold); using (var brush = new SolidBrush(grid.RowHeadersDefaultCellStyle.ForeColor)) { SizeF textSize = e.Graphics.MeasureString(rowNum, font); // 行号居中显示 float x = grid.RowHeadersWidth / 2 - textSize.Width / 2; float y = e.RowBounds.Top + (e.RowBounds.Height - textSize.Height) / 2; e.Graphics.DrawString(rowNum, font, brush, x, y); } } // 加载测试数据(替换为你自己的文本+图像数据) private void LoadTestData() { var grid = dataGridView1; for (int i = 0; i < 5; i++) { var rowData = new object[grid.Columns.Count]; for (int j = 0; j < grid.Columns.Count; j++) { // 示例数据:文本对应图像 string text = j switch { 0 => "dog", 1 => "cat", _ => "bird" }; Image img = j switch { 0 => Properties.Resources.Dog, 1 => Properties.Resources.Cat, _ => Properties.Resources.Bird }; rowData[j] = new KeyValuePair<string, Image>(text, img); } grid.Rows.Add(rowData); } } } // 自定义列头单元格,显示列号+列名 public class NumberedColumnHeaderCell : DataGridViewColumnHeaderCell { 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) { // 先绘制默认列头样式,跳过默认文本绘制 base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts & ~DataGridViewPaintParts.ContentForeground); string colNum = (OwningColumn.Index + 1).ToString(); var boldFont = new Font(cellStyle.Font.FontFamily, 10, FontStyle.Bold); using (var brush = new SolidBrush(cellStyle.ForeColor)) { // 绘制列号在左上角 graphics.DrawString(colNum, boldFont, brush, cellBounds.Left + 5, cellBounds.Top + 5); // 绘制列名在下方居中 SizeF textSize = graphics.MeasureString(formattedValue.ToString(), cellStyle.Font); PointF textPos = new PointF( cellBounds.Left + (cellBounds.Width - textSize.Width) / 2, cellBounds.Top + 25 ); graphics.DrawString(formattedValue.ToString(), cellStyle.Font, brush, textPos); } } }
三、关键说明
- 无行列数量变化:所有自定义逻辑都在单元格和表头的绘制层面实现,不会增加或修改原有行列结构。
- 布局可控:文本和图像的位置、间距都可以通过调整坐标参数自定义。
- 数据兼容性:可以将
KeyValuePair替换为你自己的实体类(比如包含Text和Image属性的类),只需在Paint方法中对应解析即可。
内容的提问来源于stack exchange,提问作者Bha01
相关产品推荐
相关产品推荐

