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

Delphi自定义DBGrid派生组件遇问题:无法实现自动换行

解决TDBGrid派生组件无法实现自动换行的问题

问题背景

受限于特殊情况无法使用EhLib、DevExpress QuantumGrid等第三方表格组件,只能基于标准DBGrid开发自定义派生组件。参考Marco Cantu《Mastering Delphi》中的示例实现后,组件始终表现为标准DBGrid的样式,无法实现文本自动换行效果。

排查过程

调试中发现,通过Canvas绘制的内容被界面元素覆盖,代码执行了绘制逻辑但最终不可见。甚至注释掉文本绘制代码、只保留Canvas.FillRect(Rect)清空单元格的操作,界面上仍显示原有数据,说明DBGrid的默认绘制流程覆盖了自定义绘制内容。

解决步骤

  1. 关闭默认绘制:将组件的DefaultDrawing属性设为False,彻底禁用DBGrid自带的单元格绘制逻辑,确保自定义DrawColumnCell方法的绘制内容能正常显示。
  2. 扩展字段类型支持:原示例仅处理了TMemoField,新增对TWideStringField、TWideMemoField类型的判断,确保宽字符串类型字段也能触发自动换行绘制。

最终完整组件代码

unit MdDbGrid;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, DB, StdCtrls, ExtCtrls, Grids, DBGrids;

type
  TMdDbGrid = class(TDBGrid)
  private
    { Private declarations }
    FLinesPerRow: Integer;
    procedure SetLinesPerRow (Value: Integer);
  protected
    { Protected declarations }
    procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;
      Column: TColumn; State: TGridDrawState); override;
    procedure  LayoutChanged; override;
  public
    { Public declarations }
    constructor Create (AOwner: TComponent); override;
  published
    { Published declarations }
    property LinesPerRow: Integer
      read FLinesPerRow write SetLinesPerRow default 1;
    // 显式发布DefaultDrawing属性,方便设计时修改
    property DefaultDrawing;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('MyComponents', [TMdDbGrid]);
end;

constructor TMdDbGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FLinesPerRow:=1;
  DefaultDrawing := False; // 创建时默认关闭默认绘制
end;

procedure TMdDbGrid.SetLinesPerRow(Value: Integer);
begin
  if Value <> FLinesPerRow then
  begin
    FLinesPerRow := Value;
    LayoutChanged;
  end;
end;

procedure TMdDbGrid.LayOutChanged;
var
  PixelsPerRow, PixelsTitle, I: Integer;
begin
  inherited LayOutChanged;

  Canvas.Font := Font;
  PixelsPerRow := Canvas.TextHeight('Wg') + 3;
  if dgRowLines in Options then
    Inc (PixelsPerRow, GridLineWidth);

  Canvas.Font := TitleFont;
  PixelsTitle := Canvas.TextHeight('Wg') + 4;
  if dgRowLines in Options then
    Inc (PixelsTitle, GridLineWidth);

  // 设置行数
  RowCount := 1 + (Height - PixelsTitle) div (PixelsPerRow * FLinesPerRow);

  // 设置每行高度
  DefaultRowHeight := PixelsPerRow * FLinesPerRow;
  RowHeights [0] := PixelsTitle;
  for I := 1 to RowCount - 1 do
    RowHeights [I] := PixelsPerRow * FLinesPerRow;

  // 发送WM_SIZE消息,让基类重新计算可见行数
  PostMessage (Handle, WM_SIZE, 0, MakeLong(Width, Height));
end;

procedure TMdDbGrid.DrawColumnCell (const Rect: TRect; DataCol: Integer;
  Column: TColumn; State: TGridDrawState);
var
  Bmp: TBitmap;
  OutRect: TRect;
begin
  if FLinesPerRow = 1 then
    inherited DrawColumnCell(Rect, DataCol, Column, State)
  else
  begin
    // 清空单元格区域
    Self.Canvas.FillRect (Rect);

    // 调整绘制区域,留出边距
    OutRect := Rect;
    InflateRect (OutRect, -2, -2);
    
    // 根据字段类型绘制内容
    if Column.Field is TGraphicField then
    begin
      Bmp := TBitmap.Create;
      try
        Bmp.Assign (Column.Field);
        Canvas.StretchDraw (OutRect, Bmp);
      finally
        Bmp.Free;
      end;
    end
    else if (Column.Field is TMemoField) or (Column.Field is TWideStringField) or (Column.Field is TWideMemoField) then
    begin 
      DrawText (Canvas.Handle, PChar (Column.Field.AsString),
        Length (Column.Field.AsString), OutRect, dt_WordBreak or dt_NoPrefix)
    end
    else 
      // 非多行字段垂直居中单行显示
      DrawText (Canvas.Handle, PChar (Column.Field.DisplayText),
        Length (Column.Field.DisplayText), OutRect,
        dt_vcenter or dt_SingleLine or dt_NoPrefix); 
  end;
end;

end.

补充说明

  • 在组件的published段显式添加了DefaultDrawing属性,方便在设计时直接修改
  • 在构造函数中默认将DefaultDrawing设为False,避免遗漏配置

内容的提问来源于stack exchange,提问作者Obsydian_ring Winds_of_hellhei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 22:30:42