TAdvStringGrid

Example 72 : Using the ICellGraphic interface for cells

vcl grid

Interfaces are a powerful way to remove code dependencies and as a result allow to better tailer code size to feature use. In TAdvStringGrid it is possible to add an interfaced object to a cell and have the interface paint the cell. This way, all kinds of small or large code can be used to paint a cell without forcing any user who is not interested in a particular graphical feature in the grid to link the code.

To achieve this, the interface ICellGraphic was created. This interface currently has only four methods:

ICellGraphic = Interface
  procedure Draw(Canvas: TCanvas;R: TRect; Col,Row: integer; 
Selected: boolean; Grid: TAdvStringGrid);
  function CellWidth: integer;
  function CellHeight: integer;
  function IsBackground: boolean;
end;

The first method Draw() is called to draw the cell Col,Row within rectangle R on the canvas Canvas. An extra parameter Selected indicates the selection state of the cell. Two functions return the desired size of the graphic in the cell. These functions are used for autosizing in the grid to adapt the cell size automatically to the size of the graphic. A function IsBackground is used to inform the grid whether text still needs to be drawn on top of the graphic or not.

To start using this interface, we need to create a class that implements the interface. In this sample, we propose 3 classes that implement the interface: TSimpleGraphicCell, TComplexGradientCell and TImageCell. TSimpleGraphicCell just demonstrates the concept. TComplexGradient & TImageCell allow to use specific GDI+ features in the grid. Note that by implementing the GDI+ features in the interfaced class, TAdvStringGrid remains completely independent of GDI+ code. So, users who prefer not to include a GDI+ dependency can keep using TAdvStringGrid as-is while users who want to exploit the extra GDI+ features can benefit from this now. The TSimpleGraphicCell class code is:

TSimpleGraphicCell = class(TInterfacedPersistent, 
ICellGraphic)
  procedure Draw(Canvas: TCanvas;R: TRect; Col,Row: integer; 
Selected: boolean; Grid: TAdvStringGrid);
  function CellWidth: integer;
  function CellHeight: integer;
  function IsBackground: boolean;
end;

function TSimpleGraphicCell.CellHeight: 
integer;
begin
  Result := 0;  // by returning zero, this graphic cell has no minimum 
cell height requirement
end;

function TSimpleGraphicCell.CellWidth: integer;
begin
  Result := 0; // by returning zero, this graphic cell has no minimum cell 
width requirement
end;

procedure TSimpleGraphicCell.Draw(Canvas: TCanvas; R: TRect; Col, Row: 
integer;
  Selected: boolean; Grid: TAdvStringGrid);
begin
  Canvas.Pen.Color := clRed;   // draw a simple diagonal line in 
the cell
  Canvas.Pen.Width := 2;
  Canvas.MoveTo(R.Left, R.Top);
  Canvas.LineTo(R.Right, R.Bottom);
end;

function 
TSimpleGraphicCell.IsBackground: boolean;
begin
  Result := true;
end;

To use the interface in a cell, this can be 
done with the code:

var 
  sg:TSimpleGraphicCell; 
begin
  sg := TSimpleGraphicCell.Create;
  AdvStringGrid1.AddInterfacedCell(2,2,sg);
end;

We have created two additional interfaced classes that now open up GDI+ capabilities for use in the grid, ie. adding complex diagonal gradients for example or draw antialiased PNG images in cells (this uses TGDIPicture & AdvGDIP, two units available in the TMS VCL UI Pack):

 TComplexGradientCell = 
class(TInterfacedPersistent, ICellGraphic)
private
  FStartColor, FEndColor: TColor;
  FGradientMode: TLinearGradientMode;
public
  procedure Draw(Canvas: TCanvas;R: TRect; Col,Row: integer; Selected: 
boolean; Grid: TAdvStringGrid);
  function CellWidth: integer;
  function CellHeight: integer;

  property StartColor: TColor read FStartColor write FStartColor;
  property EndColor: TColor read FEndColor write FEndColor;
  property GradientMode: TLinearGradientMode read FGradientMode write 
FGradientMode;
end;

TImageCell = class(TInterfacedPersistent, ICellGraphic)
private
  FPicture: TGDIPPicture;
  procedure SetPicture(const Value: TGDIPPicture);
public
  { Interface }
  procedure Draw(Canvas: TCanvas;R: TRect; Col,Row: integer; Selected: 
boolean; Grid: TAdvStringGrid);
  function CellWidth: integer;
  function CellHeight: integer;

  constructor Create;
  destructor Destroy; override;
  property Picture: TGDIPPicture read FPicture write SetPicture;
end;


{ TComplexGradientCell }

function TComplexGradientCell.CellHeight: integer;
begin
  Result := 0;
end;

function TComplexGradientCell.CellWidth: integer;
begin
  Result := 0;
end;

procedure TComplexGradientCell.Draw(Canvas: TCanvas; R: TRect; Col,
Row: integer; Selected: boolean; Grid: TAdvStringGrid);
var
  graphics : TGPGraphics;
  linGrBrush: TGPLinearGradientBrush;
begin
  // Create GDI+ canvas
  graphics := TGPGraphics.Create(Canvas.Handle);
  linGrBrush := TGPLinearGradientBrush.Create(MakeRect(r.Left,r.Top,r.Right 
- r.Left,r.Bottom - r.Top),ColorToARGB(FStartColor),ColorToARGB(FEndColor), 
FGradientMode);
  graphics.FillRectangle(linGrBrush, MakeRect(r.Left , r.Top, r.Right - 
r.Left , r.Bottom - r.Top));
  linGrBrush.Free;
  graphics.Free;
end;
function TComplexGradientCell.IsBackground: 
boolean;
begin
  Result := true;
end;


{ TImageCell }

function TImageCell.CellHeight: integer;
begin
  Result := FPicture.Height;
end;

function TImageCell.CellWidth: integer;
begin
  Result := FPicture.Width;
end;

constructor TImageCell.Create;
begin
  inherited Create;
  FPicture := TGDIPPicture.Create;
end;

destructor TImageCell.Destroy;
begin
  FPicture.Free;
  inherited;
end;

procedure TImageCell.Draw(Canvas: TCanvas; R: TRect; Col, Row: integer;
Selected: boolean; Grid: TAdvStringGrid);
begin
  Canvas.Draw(R.Left, R.Top, FPicture);
end;
function TImageCell.IsBackground: boolean;
begin
  Result := false;
end;

procedure TImageCell.SetPicture(const Value: TGDIPPicture);
begin
  FPicture.Assign(Value);
end;

The use of the TImageCell and TComplexGradientCell is done with following code:
cg := TComplexGradientCell.Create;
cg.StartColor := clBlue;
cg.EndColor := clAqua;
cg.GradientMode := LinearGradientModeHorizontal;
AdvStringGrid1.AddInterfacedCell(1,3,cg);

ig := TImageCell.Create;
ig.Picture.LoadFromFile('.\personal.png');
AdvStringGrid1.AddInterfacedCell(2,4,ig);



Delphi project & source files for downloading included in the main demos distribution for Delphi.