如何告知DataGridView自定义绘制单元格需更多空间以实现正确自动调整?
我之前在做自定义DataGridView单元格绘制的时候也踩过这个坑,分享几个亲测有效的解决方案:
核心思路:让DataGridView识别单元格的真实需求尺寸
DataGridView的自动调整功能依赖于每个单元格返回的“理想大小”,自定义绘制时默认的尺寸计算不会考虑你加的图标或者额外间距,所以我们需要手动告诉它真实的空间需求。
1. 重写自定义单元格的GetPreferredSize方法
这是最关键的一步!DataGridView在自动调整列宽/行高时,会调用每个单元格的这个方法来获取它的理想尺寸。你需要把图标、文本、间距这些元素的空间都计算进去,返回准确的Size值。
举个代码例子(假设你要绘制一个16x16的图标+文本):
public class CustomIconTextCell : DataGridViewTextBoxCell { // 图标尺寸和间距可以根据你的需求调整 private readonly Size _iconSize = new Size(16, 16); private readonly int _iconTextGap = 4; protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) { // 获取单元格文本内容,为空时用空字符串兜底 string cellText = Value?.ToString() ?? string.Empty; // 计算文本需要的尺寸:扣除图标和间距的宽度,高度不限制 Size textSize = TextRenderer.MeasureText( graphics, cellText, cellStyle.Font, new Size(constraintSize.Width - _iconSize.Width - _iconTextGap, int.MaxValue), TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl); // 总宽度 = 图标宽度 + 间距 + 文本宽度 + 单元格左右内边距 int totalWidth = _iconSize.Width + _iconTextGap + textSize.Width + cellStyle.Padding.Left + cellStyle.Padding.Right; // 总高度取图标和文本高度的最大值,再加上上下内边距 int totalHeight = Math.Max(_iconSize.Height, textSize.Height) + cellStyle.Padding.Top + cellStyle.Padding.Bottom; // 返回计算后的理想尺寸 return new Size(totalWidth, totalHeight); } }
2. 开启DataGridView的自动调整功能
光重写方法还不够,得让DataGridView启用自动调整:
- 列宽自动调整:
// 所有单元格都参与计算列宽,也可以选DisplayedCells只计算可见单元格 dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; - 行高自动调整(如果文本可能换行或者图标高度超过默认行高):
dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
3. 自定义绘制时的文本省略号处理
在你的Paint方法里,要确保文本绘制区域和GetPreferredSize的计算逻辑对应上,同时用TextRenderer.DrawText加上省略号参数:
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处理背景、边框等默认绘制逻辑 base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts); string text = formattedValue?.ToString() ?? string.Empty; // 绘制图标(替换成你实际的图标绘制代码,比如DrawIcon/DrawImage) Point iconPos = new Point( cellBounds.Left + cellStyle.Padding.Left, cellBounds.Top + (cellBounds.Height - _iconSize.Height) / 2); // graphics.DrawIcon(yourIcon, new Rectangle(iconPos, _iconSize)); // 计算文本绘制区域:扣除图标、间距和内边距 Rectangle textRect = new Rectangle( cellBounds.Left + cellStyle.Padding.Left + _iconSize.Width + _iconTextGap, cellBounds.Top + cellStyle.Padding.Top, cellBounds.Width - cellStyle.Padding.Left - cellStyle.Padding.Right - _iconSize.Width - _iconTextGap, cellBounds.Height - cellStyle.Padding.Top - cellStyle.Padding.Bottom); // 绘制文本,超出边界时显示省略号 TextRenderer.DrawText( graphics, text, cellStyle.Font, textRect, cellStyle.ForeColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis); }
4. 额外小提示
- 如果单元格内容是动态更新的,记得调用
dataGridView1.InvalidateCell(rowIndex, columnIndex)或者dataGridView1.AutoResizeColumns()来触发重新计算尺寸 - 如果你用的是自定义列,要把列的
CellTemplate设置为你的自定义单元格:var customColumn = new DataGridViewColumn { CellTemplate = new CustomIconTextCell() }; dataGridView1.Columns.Add(customColumn); - 尽量不要在
GetPreferredSize里做耗时操作,因为自动调整时会频繁调用这个方法,影响界面流畅度
内容的提问来源于stack exchange,提问作者Tobias Wollgam
相关产品推荐
相关产品推荐

