TAdvStringGrid

Example 74 : Embedding charts in TAdvStringGrid

vcl grid

In sample 72, it was shown how interfaces allowed to use any kind of graphic control inside TAdvStringGrid. The main enabler for this is the ICellGraphic interface:

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;

In this sample, it will be shown how a class TAdvChart, internally used and available in TMS TAdvChartView can be used to draw charts within a cell or cells of TAdvStringGrid. A TAdvChart is the class within a chartview pane that is responsible for the drawing of the chart. This class is used here in a TInterfacedPersistent descending class that implements the ICellGraphic interface:

TChartCell = class(TInterfacedPersistent, ICellGraphic)
private
  FChart: TAdvGDIPChart;
  procedure SetChart(const Value: TAdvGDIPChart);
public
{ Interface }
  procedure Draw(Canvas: TCanvas;R: TRect; Col,Row: integer; Selected: boolean; Grid: TAdvStringGrid);
  function CellWidth: integer;
  function CellHeight: integer;
  function IsBackground: boolean;

  constructor Create;
  destructor Destroy; override;
  property Chart: TAdvGDIPChart read FChart write SetChart;
end;


All this class does is create an instance of TAdvGDIChart (can be TAdvChart for a non GDI+ version). In the Draw method implementation it uses this chart to perform the drawing. The CellWidth / CellHeight function implementations return 0 as the chart will size automatically with the cell dimensions instead of having a fixed size. The IsBackground function returns false as we do not assume it is desirable to put text on top of the chart.

The implementation as such looks like:

{ TChartCell }

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

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

constructor TChartCell.Create;
begin
  inherited Create;
  FChart := TAdvGDIPChart.Create(self);
end;

destructor TChartCell.Destroy;
begin
  FChart.Free;
  inherited;
end;

procedure TChartCell.Draw(Canvas: TCanvas; R: TRect; Col, Row: integer;
Selected: boolean; Grid: TAdvStringGrid);
var
  dr: trect;
begin
  dr := r;
  dr.Left := dr.Left + 2;
  FChart.Draw(Canvas,dr,1,1,true);
end;

When this class is available, it is easy to start using it to insert a chart in a grid cell or cells. To do this, create an instance of TChartCell and add it to the grid via the grid.AddInterfacedCell method. Make sure to destroy this instance for example from the FormClose event.

procedure TForm1.FormCreate(Sender: TObject);
var
 cc: TChartCell;
begin
  cc := TChartCell.Create;
  AdvStringGrid1.AddInterfacedCell(2,2,cc);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
AdvStringGrid1.GetInterfacedCell(2,2).Free;
end;

When the ChartCell is available, the chart properties can be set via ChartCell.Chart and the chart data can be added via ChartCell.Chart.Series as in the sample code below:

with cc.Chart.Series.Add do
begin
  // choose chart type
  ChartType := ctArea;

  // fill with sinusoidal data
  for i := 1 to 100 do
    AddSinglePoint(20 * sin(i /PI));

  // select range of data to display
  cc.Chart.Range.RangeFrom := 0;
  cc.Chart.Range.RangeTo := 99;

  // hide default legend
  cc.Chart.Legend.Visible := false;

  // set chart x-axis size & fontsize
  cc.Chart.XAxis.Size := 12;
  cc.Chart.XAxis.Font.Size := 7;

  // set serie x-axis
  XAxis.MajorFont.Size := 7;
  XAxis.MajorUnitSpacing := 0;
  XAxis.MajorUnit := 1;

  // set x-axis tickmark
  Xaxis.TickMarkSize := 2;
  XAxis.TickMarkWidth := 1;
  XAxis.TickMarkColor := clBlack;

  // set chart y-axis size & position
  cc.Chart.YAxis.Position := yRight;
  cc.Chart.YAxis.Size := 20;

  // enable auto units on y-axis
  cc.Chart.YAxis.AutoUnits := true;

  // set serie y-axis
  YAxis.Position := yRight;
  YAxis.MajorUnitSpacing := 8;
  YAxis.MajorFont.Size := 6;
  YAxis.MajorUnit := 1;

  // set y-axis tickmark
  YAxis.TickMarkColor := clRed;
  YAxis.TickMarkSize := 2;
  YAxis.TickMarkWidth := 1;

  // enable auto ranging
  AutoRange := arEnabled;
end;

The chart can be updated at any time this way. After updating the chart programmatically, force a repaint of the chart by calling grid.RepaintCell(col,row);

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