如何将CR/LF放入TStringGrid单元格中?

8
我希望将一行文本作为表头固定,但是这些文本比较长,所以我想增加行高并在单元格文本中插入CR/LF。
通过搜索,发现了以下解决方案(也是我在搜索之前首先想到的),但似乎不起作用。有什么想法吗?
Grid.Cells[2,3] := 'This is a sample test' + #13#10 + 'This is the second line';

发生的情况是单元格包含这是一个示例测试这是第二行

如果有任何区别,我正在使用Delphi 7。

[悬赏]“我的错。我两年前实际上授予了这个答案,没有检查,现在发现答案不起作用。对于被误导的任何人表示歉意。这是一个经常被问到,通常回答错误的问题。”

我认为我们要使用OnDrawCell,但想象我们还必须增加包含单元格的字符串网格行的高度。

我将颁发代码或FOSS VCL组件的答案。

[更新]必须与Delphi XE2 Starter版配合使用


3
TStringGrid的RowHeights[#]不能解决问题吗?编辑:我看到答案中链接的代码中,已经使用了RowHeights。 - Sertac Akyuz
一些替代想法(可能不是您想要的):当用户进入TStringGrid的编辑模式时,您可以显示自己的编辑器(一个小TMemo)。 - Gabriel
2个回答

24

TStringGrid使用Canvas.TextRect,而Canvas.TextRect又使用了ExtTextOut,而这个函数本身不支持多行文本的绘制。

你需要在OnDrawCell事件处理程序中自己使用WinAPI的DrawText例程来绘制多行文本。例如,可以参考这个答案中如何使用DrawText进行多行文本的绘制,以及这个最近的答案中如何在OnDrawCell中实现自定义绘制:

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
  private
    procedure FillWithRandomText(AGrid: TStringGrid);
    procedure UpdateRowHeights(AGrid: TStringGrid);
  end;

procedure TForm1.FillWithRandomText(AGrid: TStringGrid);
const
  S = 'This is a sample'#13#10'text that contains'#13#10'multiple lines.';
var
  X: Integer;
  Y: Integer;
begin
  for X := AGrid.FixedCols to AGrid.ColCount - 1 do
    for Y := AGrid.FixedRows to AGrid.RowCount - 1 do
      AGrid.Cells[X, Y] := Copy(S, 1, 8 + Random(Length(S) - 8));
  UpdateRowHeights(AGrid);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FillWithRandomText(StringGrid1);
end;

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
begin
  with TStringGrid(Sender) do
    if Pos(#13#10, Cells[ACol, ARow]) > 0 then
    begin
      Canvas.FillRect(Rect);
      Inc(Rect.Left, 2);
      Inc(Rect.Top, 2);
      DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]), -1, Rect,
        DT_NOPREFIX or DT_WORDBREAK);
    end;
end;

procedure TForm1.UpdateRowHeights(AGrid: TStringGrid);
var
  Y: Integer;
  MaxHeight: Integer;
  X: Integer;
  R: TRect;
  TxtHeight: Integer;
begin
  for Y := AGrid.FixedRows to AGrid.RowCount - 1 do
  begin
    MaxHeight := AGrid.DefaultRowHeight - 4;
    for X := AGrid.FixedCols to AGrid.ColCount - 1 do
    begin
      R := Rect(0, 0, AGrid.ColWidths[X] - 4, 0);
      TxtHeight := DrawText(AGrid.Canvas.Handle, PChar(AGrid.Cells[X, Y]), -1,
        R, DT_WORDBREAK or DT_CALCRECT);
      if TxtHeight > MaxHeight then
        MaxHeight := TxtHeight;
    end;
    AGrid.RowHeights[Y] := MaxHeight + 4;
  end;
end;

默认StringGrid


还有其他能够绘制多行文本的StringGrid组件。例如,我自己编写的这个(下载源代码:NLDStringGrid),可能会得到以下结果:

NLDStringGrid

var
  R: TRect;
begin
  NLDStringGrid1.Columns.Add;
  NLDStringGrid1.Columns.Add;
  NLDStringGrid1.Cells[1, 1] := 'Sample test'#13#10'Second line';
  NLDStringGrid1.Columns[1].MultiLine := True;
  NLDStringGrid1.AutoRowHeights := True;
  SetRect(R, 2, 2, 3, 3);
  NLDStringGrid1.MergeCells(TGridRect(R), True, True);
  NLDStringGrid1.ColWidths[2] := 40;
  NLDStringGrid1.Cells[2, 2] := 'Sample test'#13#10'Second line';
end;

+1 @NGLN 我现在会下载并尝试它。使用上有任何限制吗? - Mawg says reinstate Monica
编译失败 - 找不到 DesignEditors.dcu - 可能是因为我正在使用 XE2 Starter 版本? - Mawg says reinstate Monica
@Mawg 可能是这样,但我不确定。你把包文件转换成XE2了吗?注意requires DesignIde这一行!否则试试运行时。 - NGLN
上面的代码(不是你的包),运行得很好 - 除了...如果一行中的所有条目都是空白的,那么该行将显示为一个像素高度。这可能是默认行高吗?尝试这个 - 在FillWithRandomText()中删除循环并分配单个单元格(例如Cells[3,3])。我认为你会在这个问题上获得赏金,但如果你能调整那个空白行的问题就更好了。 - Mawg says reinstate Monica
1
我将 UpdateRowHeights() 中的 MaxHeight := 0; 更改为 MaxHeight := AGrid.DefaultRowHeight;,现在空行看起来合理了。 - Mawg says reinstate Monica

5
TStringGrid 的默认渲染器不支持多行。通过将 TStringGrid 设置为 OwnerDraw 模式(通过调用 OnDrawCell 事件),您可以按照自己的喜好呈现每个单元格。
请查看 this,了解帮助先前用户的示例。
已插入链接引用代码:
procedure DrawSGCell(Sender : TObject; C, R : integer; Rect : TRect;
          Style : TFontStyles; Wrap : boolean; Just : TAlignment;
          CanEdit : boolean);
  { draws formatted contents in string grid cell at col C, row R;
    Style is a set of fsBold, fsItalic, fsUnderline and fsStrikeOut;
    Wrap invokes word wrap for the cell's text; Just is taLeftJustify,
    taRightJustify or taCenter; if CanEdit false, cell will be given 
    the background color of fixed cells; call this routine from 
    grid's DrawCell event }
var
  S        : string;
  DrawRect : TRect;
begin
  with (Sender as tStringGrid), Canvas do begin
    { erase earlier contents from default drawing }
    if (R >= FixedRows) and (C >= FixedCols) and CanEdit then
      Brush.Color:= Color
    else
      Brush.Color:= FixedColor;
    FillRect(Rect);
    { get cell contents }
    S:= Cells[C, R];
    if length(S) > 0 then begin
      case Just of
        taLeftJustify  : S:= ' ' + S;
        taRightJustify : S:= S + ' ';
        end;
      { set font style }
      Font.Style:= Style;
      { copy of cell rectangle for text sizing }
      DrawRect:= Rect;
      if Wrap then begin
        { get size of text rectangle in DrawRect, with word wrap }
        DrawText(Handle, PChar(S), length(S), DrawRect,
          dt_calcrect or dt_wordbreak or dt_center);
        if (DrawRect.Bottom - DrawRect.Top) > RowHeights[R] then begin
          { cell word-wraps; increase row height }
          RowHeights[R]:= DrawRect.Bottom - DrawRect.Top;
          SetGridHeight(Sender as tStringGrid);
          end
        else begin
          { cell doesn't word-wrap }
          DrawRect.Right:= Rect.Right;
          FillRect(DrawRect);
          case Just of
            taLeftJustify  : DrawText(Handle, PChar(S), length(S), DrawRect,
                               dt_wordbreak or dt_left);
            taCenter       : DrawText(Handle, PChar(S), length(S), DrawRect,
                               dt_wordbreak or dt_center);
            taRightJustify : DrawText(Handle, PChar(S), length(S), DrawRect,
                               dt_wordbreak or dt_right);
            end;
          end
        end
      else
        { no word wrap }
        case Just of
          taLeftJustify  : DrawText(Handle, PChar(S), length(S), DrawRect,
                             dt_singleline or dt_vcenter or dt_left);
          taCenter       : DrawText(Handle, PChar(S), length(S), DrawRect,
                             dt_singleline or dt_vcenter or dt_center);
          taRightJustify : DrawText(Handle, PChar(S), length(S), DrawRect,
                             dt_singleline or dt_vcenter or dt_right);
          end;
      { restore no font styles }
      Font.Style:= [];
      end;
    end;
end;

我认为这对您来说会很好...


1
@Mawg,这是一个(在很多方面)你不应该编写代码的示例(除了它可以完成NGLN指出的任务)。我的建议是,不要使用它... - TLama
3
第一次调用DrawText(带有DT_CALCRECT标志)计算绘制文本所需的矩形,该矩形考虑了多行文本。 - Sertac Akyuz
1
@Mawg - 是的,这将计算出3行矩形。此外,如果任何一行超过了右边缘,它将被换行(因为DT_WORDBREAK标志),因此您可能会得到更多的行。 - Sertac Akyuz
1
@Mawg 这段代码来自2003年的一个论坛。LU RD在这里粘贴了代码,我只是链接到了原始帖子。然而,我不是Delphi程序员(我使用C++ Builder),并且使用与Delphi用户相同的VCL库。可能自2003年以来TStringGrid组件已经发生了变化(他们不是第一次移动东西)。我的猜测是调用了SetGridHeight()来强制单元格重新绘制,因此再次进入此例程,但是这次单元格高度将适合整个文本并将被绘制。 - Max Kielland
1
如果代码在没有SetGridHeight()的情况下正常工作,则它会在底层VCL中完成工作。我同意这不是调整单元格高度的好地方,应该在为网格分配新值的同时完成此操作。有时您可能不希望单元格大小发生变化。但是,您可以将高度计算的上限值应用于只允许其达到最大高度。 - Max Kielland
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接