1. Technology

Owner Drawing The String Grid

TStringGrid Drawing: Background, Alignment, Colors

By

Owner Drawing The String Grid

TStringGrid Delphi Owner Drawing

TStringGrid Delphi Owner Drawing
Article submitted by Alan Lloyd

For StringGrids with simple content the default drawing action is sufficient, but when various aligns or different colors need to be included in the stringgrid, drawing code must be written in the OnDrawCell event to support owner draw. Owner draw does not need to be specifically set (as one has to with a TListBox), one just places drawing code in the event handler and it is called by the class code after drawing the grid.

I will cover the following in this note:

  • Drawing the cell in differing conditions
  • Writing the text with differing alignments
  • Providing a background to the stringgrid from a bitmap.
When you draw the cell the OnDrawCell event handler provides the parameters of :
StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; ARect: TRect; State: TGridDrawState);
The parameters are mainly self-evident except that ARect is the rectangle of the cell to be drawn in TStringGrid axes, and State is a:
TGridDrawState = set of (gdSelected, gdFocused, gdFixed).
Being a set, the value of the set must be enclosed in square brackets, and tests for content must use the “in” operator.

For each cell I must paint the background of the cell, and then place the text. Assuming I do not want to change the colour or font I can code :

StringGrid.Canvas.FillRect(ARect);
StringGrid.Canvas.TextOut(ARect.X + LM, ARect.Y + TM, StringGrid.Cells(ACol, ARow));
where LM & TM are left & top margins respectively (usually 2 pixels).

Let’s look at the code for a more complex stringgrid display which has left, centre & right align with different fonts and display of the selected row.

procedure TmyForm.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; ARect: TRect; State: TGridDrawState);
var
  TW, TL : integer; //Text Width, Text Left
const
  LM = 2;   // Left Margin
  TM = 2;   // Top Margin
begin
  if (gdSelected in State) then 
  begin
    StringGrid.Canvas.Brush.Color := clHighlight;
    StringGrid.Canvas.Font.Color := clHighlightText;
  end
  else 
  begin
    StringGrid.Canvas.Brush.Color := clWindow;
    StringGrid.Canvas.Font.Color := clWindowText;
  end; {if (gdSelected in State) else}

  TW := StringGrid.Canvas.TextWidth(StringGrid.Cells[Col, Row]);

  StringGrid.Canvas.FillRect(ARect);

  case Col of
    0 : TL := LM;   // left align
    1 : TL := (ARect.Left + ARect.Right - TW)/2;   // centre align
    2 : TL := ARect.Right - TW - LM;   // right align
  end; {case Col}
	
  StringGrid.Canvas.TextOut(TL, ARect.Top + TM, StringGrid.Cells(ACol, ARow));
end;

Now to add a bitmap background. We first of all need a bitmap to hold the background picture. Note that one needs a fairly light tone in the bitmap to save swamping the text. The size of the bitmap must be no less than the stringgrid size. It does not matter if it is 10 - 20 pixels bigger than the stringgrid. We should place the bitmap in a resource which we add to the Delphi code.

One can write a resource contents (*.rc) file specifying the resource and use the BRCC32.exe program to build the *.res resource file. I create the bitmap in a graphic file and name it Background.bmp.

I create a text file named Backgnd.rc containing the line :

BackGnd RT_BITMAP "Background.bmp"
… and save that to the same folder as the bitmap file. If the bitmap _must_ be in a different folder from the *.rc file (& hence the *.res file) put the file path name in the inverted commas in the text.

Then create the *.res file by running a command line of :

BRCC32 BackGnd.rc -v
This will create a file of BackGnd.res & it needs to be in the same folder as the Delphi code.

There may be a more sophisticated method of creating a resource file but the above works !

Place the line {$R 'BackGnd.res'} in the implementation section of you code. Also add a global variable BM : TBitmap in the implementation section.

In the FormCreate event place the following code to load the bitmap to a TBitmap :

  BM := TBitmap.Create;
  {load BM from resource}
  BM.Handle := LoadBitmap(HInstance, 'BackGnd');
  {BM is copied into stringgrid in StringGrid1DrawCell }
And of course BM.Free (so no memory is wasted); in the FormClose event.

Then in the StringGridDrawCell event handler we draw an ARect sized portion of the bitmap in each cell by replacing the

StringGrid.Canvas.FillRect(ARect);

line with :

StringGrid.Canvas.CopyRect(ARect, BM.Canvas, ARect);

That’s it. Quick and easy :)

©2014 About.com. All rights reserved.