You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何告知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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:50:57