3

Hi does anyone know if it is possible to display a picture as a background to a string grid, Or is anyone aware of any free Grid component that can do this.

Thanks

colin

colin
  • 2,983
  • 6
  • 41
  • 49

3 Answers3

12

You could use a TDrawGrid (or a TStringGrid), which supports owner-drawing, and do

procedure TForm1.FormCreate(Sender: TObject);
begin
  FBg := TBitmap.Create;
  FBg.LoadFromFile('C:\Users\Andreas Rejbrand\Pictures\Sample.bmp');
end;

where FBg is a TBitmap (in the form class, for instance), and then do

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  r: TRect;
begin
  if not (Sender is TStringGrid) then Exit;
  BitBlt(TStringGrid(Sender).Canvas.Handle,
         Rect.Left,
         Rect.Top,
         Rect.Right - Rect.Left,
         Rect.Bottom - Rect.Top,
         FBg.Canvas.Handle,
         Rect.Left,
         Rect.Top,
         SRCCOPY);
  if gdSelected in State then
    InvertRect(TStringGrid(Sender).Canvas.Handle, Rect);
  r := Rect;
  TStringGrid(Sender).Canvas.Brush.Style := bsClear;
  DrawText(TStringGrid(Sender).Canvas.Handle,
           TStringGrid(Sender).Cells[ACol, ARow],
           length(TStringGrid(Sender).Cells[ACol, ARow]),
           r,
           DT_SINGLELINE or DT_VCENTER or DT_END_ELLIPSIS);
end;

Sample Screenshot Sample Screenshot Sample Screenshot

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • 5
    Good answer, but this will work with TStringGrid too, since it inherits from TDrawGrid. – GolezTrol Mar 12 '11 at 21:49
  • @David, et al.: I am not sure: First I thought that I need to make sure that I don't try to make `BitBlt` read pixels that aren't there in the source bitmap, but it seems like the function is able to handle input of this kind in the most kind way there is, namely correcting for the "incorrect" input (like `Copy`, where you often specify `MaxInt` as the length of the string). Could you please enlighten me on this issue? – Andreas Rejbrand Mar 12 '11 at 22:42
  • I presume this only works for the created cell of the TDrawGrid. What if the requirement is to have the background image extend into the parts of the TDrawGrid that aren't covered by cells, i.e. after the last row or columns? – rossmcm Mar 14 '11 at 21:08
  • @rossmcm: See the additional answer. – NGLN May 25 '11 at 18:06
4

While actually answering here the explicit question of rossmcm in his comment to the code of Andreas Rejbrand, it also complements hís answer to the original question.

Drawing the image beyond the grid boundary, but still within the StringGrid control bounds could be achieved as follows:

type
  TStringGrid = class(Grids.TStringGrid)
  private
    FGraphic: TGraphic;
    FStretched: Boolean;
    function BackgroundVisible(var ClipRect: TRect): Boolean;
    procedure PaintBackground;
  protected
    procedure Paint; override;
    procedure Resize; override;
    procedure TopLeftChanged; override;
  public
    property BackgroundGraphic: TGraphic read FGraphic write FGraphic;
    property BackgroundStretched: Boolean read FStretched write FStretched;
  end;

  TForm1 = class(TForm)
    StringGrid: TStringGrid;
    Image: TImage;
    procedure FormCreate(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TStringGrid }

function TStringGrid.BackgroundVisible(var ClipRect: TRect): Boolean;
var
  Info: TGridDrawInfo;
  R: TRect;
begin
  CalcDrawInfo(Info);
  SetRect(ClipRect, 0, 0, Info.Horz.GridBoundary, Info.Vert.GridBoundary);
  R := ClientRect;
  Result := (ClipRect.Right < R.Right) or (ClipRect.Bottom < R.Bottom);
end;

procedure TStringGrid.Paint;
begin
  inherited Paint;
  PaintBackground;
end;

procedure TStringGrid.PaintBackground;
var
  R: TRect;
begin
  if (FGraphic <> nil) and BackgroundVisible(R) then
  begin
    with R do
      ExcludeClipRect(Canvas.Handle, Left, Top, Right, Bottom);
    if FStretched then
      Canvas.StretchDraw(ClientRect, FGraphic)
    else
      Canvas.Draw(0, 0, FGraphic);
  end;
end;

procedure TStringGrid.Resize;
begin
  inherited Resize;
  PaintBackground;
end;

procedure TStringGrid.TopLeftChanged;
begin
  inherited TopLeftChanged;
  PaintBackground;
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Usage: 
  StringGrid.BackgroundGraphic := Image.Picture.Graphic;
  StringGrid.BackgroundStretched := True;
end;

If you want to draw the image in the cells as well, then combine both techniques. That they do not follow the same approach, for Andreas uses events where I declare a descendant, should not lead to great difficulty with merging.

NGLN
  • 43,011
  • 8
  • 105
  • 200
  • note to others, moving the tstringgrid code to a separate unit (say nglncontrol) works fine with designtime forms, if you either put the unit last in the USES clause, or use a typealias (type TStringGrid=nglncontrol.TStringGrid before the form declaration). I use the latter. – Marco van de Voort Jan 09 '13 at 14:21
1

Yes, it is possible. TStringGrid inherits from TDrawGrid and does all drawing on its own. You can use the OnDrawCell event to do custom drawing.

GolezTrol
  • 114,394
  • 18
  • 182
  • 210