Search code examples
delphidelphi-xe2

How to put CR/LF into a TStringgrid cell?


I want to have one fixed row as a header, but the texts are rather long, so I'd like to increase the row height and insert CR/LF into the cell text.

Googling shows this as a solution (and it's the first thing I thought of before googling), but it doesn't seem to work. Any ideas?

Grid.Cells[2,3] := 'This is a sample test' + #13#10 + 'This is the second line';

What happens is that the cell contains This is a sample testThis is the second line

I am using Delphi 7 if it makes any difference.

[Bounty] "My bad. I actually awarded this an answer two years ago without checking and now find that the answer did not work. Apologies to anyone who was misled. This is a frequently asked, often wrongly answered question."

I presume that we are looking to use OnDrawCell, but imagine that we would also have to increase the height of the string grid row which contains the cell.

I will award the answer for either code or a FOSS VCL component.

[Update] must work with Delphi XE2 Starter edition


Solution

  • TStringGrid uses Canvas.TextRect, which uses ExtTextOut, which in turn does not support drawing of multiline text.

    You have to draw this yourself in an OnDrawCell event handler with WinAPI's DrawText routine. See for example this answer on how to use DrawText for multiline text, and this recent answer on how to implement custom drawing in 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;
    

    Default StringGrid


    There are also other StringGrid components able of drawing multiline text. For instance, this one which I wrote myself (download source: NLDStringGrid) with possibly this result:

    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;