Search code examples
lazarustext-renderingtstringgrid

How can you change the text orientation in cells in the fixed rows in a Delphi TStringGrid


I have a standard TStringGrid on a form. I have one Fixed Row in the grid that contains a number of columns, which are all TGridColumns objects. I have set the column titles using the object inspector and the default orientation is horizontal. Is there any way you can make the orientation vertical (like you can in cells in Excel)?


Solution

  • Here's how to render the first row's text vertically in Lazarus:

    unit Unit1; 
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Grids,
      StdCtrls;
    
    type
      TStringGrid = class(Grids.TStringGrid)
      protected
        procedure DrawCellText(ACol, ARow: Integer; ARect: TRect;
          AState: TGridDrawState; AText: String); override;
      end;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        StringGrid1: TStringGrid;
        procedure Button1Click(Sender: TObject);
      private
        { private declarations }
      public
        { public declarations }
      end; 
    
    var
      Form1: TForm1; 
    
    implementation
    
    {$R *.lfm}
    
    procedure TStringGrid.DrawCellText(ACol, ARow: Integer; ARect: TRect;
      AState: TGridDrawState; AText: String);
    var
      TextPosition: TPoint;
    begin
      if ARow = 0 then
      begin
        Canvas.Font.Orientation := 900;
        TextPosition.X := ARect.Left +
          ((ARect.Right - ARect.Left - Canvas.TextHeight(AText)) div 2);
        TextPosition.Y := ARect.Bottom -
          ((ARect.Bottom - ARect.Top - Canvas.TextWidth(AText)) div 2);
        Canvas.TextOut(TextPosition.X, TextPosition.Y, AText);
      end
      else
        inherited DrawCellText(ACol, ARow, ARect, AState, AText);
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      I: Integer;
      GridColumn: TGridColumn;
    begin
      for I := 0 to 4 do
      begin
        GridColumn := StringGrid1.Columns.Add;
        GridColumn.Width := 24;
        GridColumn.Title.Font.Orientation := 900;
        GridColumn.Title.Layout := tlBottom;
        GridColumn.Title.Caption := 'Column no. ' + IntToStr(I);
      end;
      StringGrid1.RowHeights[0] := 80;
    end;
    
    end.
    

    Here's how to render the first row's text of the TStringGrid vertically in Delphi:

    I would prefer to use the overriden DrawCell procedure because it seems to me as the easiest way to go because if you want to render the text simply in the OnDrawCell event then you should consider:

    • if you'll have the DefaultDrawing set to True then the text will already be rendered when the OnDrawCell event is fired, so here I would recommend e.g. to store the cell captions in a separate variable, not into Cells property so then no text will be rendered and you can draw your own stored captions vertically
    • if you'll have the DefaultDrawing set to False then you'll have to draw the whole cell by your own, including the 3D border, what is IMHO not so cool, and I would personally prefer to let the control draw the background for us

    Here is the Delphi code which uses the overriden DrawCell procedure. The text is being centered inside of the cell rectangle; please note that I haven't used the DrawTextEx for text size measurement because this function doesn't take the changed font orientation into account.

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, Grids;
    
    type
      TStringGrid = class(Grids.TStringGrid)
      protected
        procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
          AState: TGridDrawState); override;
      end;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        StringGrid1: TStringGrid;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TStringGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
      AState: TGridDrawState);
    var
      LogFont: TLogFont;
      TextPosition: TPoint;
      NewFontHandle: HFONT;
      OldFontHandle: HFONT;
    begin
      if ARow = 0 then
      begin
        GetObject(Canvas.Font.Handle, SizeOf(LogFont), @LogFont);
        LogFont.lfEscapement := 900;
        LogFont.lfOrientation := LogFont.lfEscapement;
        NewFontHandle := CreateFontIndirect(LogFont);
        OldFontHandle := SelectObject(Canvas.Handle, NewFontHandle);
        TextPosition.X := ARect.Left +
          ((ARect.Right - ARect.Left - Canvas.TextHeight(Cells[ACol, ARow])) div 2);
        TextPosition.Y := ARect.Bottom -
          ((ARect.Bottom - ARect.Top - Canvas.TextWidth(Cells[ACol, ARow])) div 2);
        Canvas.TextRect(ARect, TextPosition.X, TextPosition.Y, Cells[ACol, ARow]);
        NewFontHandle := SelectObject(Canvas.Handle, OldFontHandle);
        DeleteObject(NewFontHandle);
      end
      else
        inherited DrawCell(ACol, ARow, ARect, AState);
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      I: Integer;
    begin
      for I := 0 to StringGrid1.ColCount - 1 do
      begin
        StringGrid1.ColWidths[I] := 24;
        StringGrid1.Cells[I, 0] := 'Column no. ' + IntToStr(I);
      end;
      StringGrid1.RowHeights[0] := 80;
    end;
    
    end.
    

    And here's how it looks like:

    enter image description here