Search code examples
delphichartsruntimeteechart

how to add a chartseries to a dbchart during runtime in delphi


I need to add chartseries during runtime cause I don't know how many chart series I'll have at design time, so I wrote some code to add it. However for some reason it's not working...

Most of my charts require only 1 chartseries, except 1 which requires 2. So I could add an extra form with 2 chartseries on, but that would clash my design for this particular form to be generic in what kind of data it can display. So right now, I have 1 chartseries during design time + 1 extra during runtime. The chartseries added in design mode works perfectly. The one I added during runtime is pretty much an exact copy of the properties of the chart in the form file (.dfm).

Form behind:

object frmChartTest: TfrmChartTest
  Left = 0
  Top = 0
  Caption = 'Test'
  ClientHeight = 299
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnShow = FormShow
  PixelsPerInch = 96
  TextHeight = 13
  object chtTestChart: TDBChart
    Left = 0
    Top = 0
    Width = 635
    Height = 299
    Title.Text.Strings = (
      'Title Chart')
    BottomAxis.DateTimeFormat = 'dd/MM/yyyy'
    BottomAxis.Increment = 1.000000000000000000
    BottomAxis.LabelStyle = talValue
    BottomAxis.Title.Caption = 'Title bottom axe'
    LeftAxis.Automatic = False
    LeftAxis.AutomaticMaximum = False
    LeftAxis.AutomaticMinimum = False
    LeftAxis.ExactDateTime = False
    LeftAxis.Maximum = 100.000000000000000000
    LeftAxis.Title.Caption = 'Title Y-ax'
    Legend.Visible = False
    View3D = False
    View3DOptions.Elevation = 344
    Zoom.Pen.Mode = pmNotXor
    Align = alClient
    BevelOuter = bvNone
    Color = clWhite
    TabOrder = 0
    DefaultCanvas = 'TGDIPlusCanvas'
    ColorPaletteIndex = 13
  end
end

actual code:

unit ChartTest;

interface

uses
  Data.DB, FireDAC.Comp.Client, Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.DateUtils,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VclTee.TeeGDIPlus, VclTee.TeEngine, Vcl.ExtCtrls, VclTee.TeeProcs,
  VclTee.Chart, VclTee.DBChart;

type
  TfrmChartTest = class(TForm)
    chtTestChart: TDBChart;
    procedure FormShow(Sender: TObject);
  private
    procedure CreateField(MemoryDataSet: TFDMemTable; AFieldName: string; AFieldType: TFieldType; ASize: integer);
    procedure AddValues(FieldNameXAxe, FieldNameYAxe: string; LineColor: TColor);
    function GetData(): TFDMemTable;
  end;

var
  frmChartTest: TfrmChartTest;

implementation

{$R *.dfm}

const
  dateField: string = 'date1';
  intField1: string = 'int1';
  intField2: string = 'int2';

{ TfrmChartTest }

procedure TfrmChartTest.AddValues(FieldNameXAxe, FieldNameYAxe: string;
  LineColor: TColor);
var
  chartSeries: TChartSeries;
begin
  chartSeries := TChartSeries.Create(chtTestChart);
  with chartSeries do begin
    ParentChart := chtTestChart;
    Marks.Style := smsXValue;
    DataSource := GetData();
    SeriesColor := LineColor;
    XLabelsSource := FieldNameXAxe;
    Pen.Color := LineColor;
    Pen.Width := 3;
    XValues.DateTime := True;
    XValues.Name := 'X';
    XValues.Order := loAscending;
    XValues.ValueSource := FieldNameXAxe;
    YValues.Name := 'Y';
    YValues.Order := loNone;
    YValues.ValueSource := FieldNameYAxe;
    DrawBetweenPoints := True;
  end;
  chtTestChart.AddSeries(chartSeries);
  chartSeries.Active := True;
end;

procedure TfrmChartTest.CreateField(MemoryDataSet: TFDMemTable;
  AFieldName: string; AFieldType: TFieldType; ASize: integer);
begin
  with MemoryDataSet.FieldDefs.AddFieldDef do begin
    name := AFieldName;
    DataType := AFieldType;
    if ASize > 0 then begin
      Size := ASize;
    end;
  end;
end;

procedure TfrmChartTest.FormShow(Sender: TObject);
begin
  AddValues(dateField, intField1, clRed);
  AddValues(dateField, intField2, clBlue);
  chtTestChart.LeftAxis.Minimum := 45;
  chtTestChart.LeftAxis.Maximum := 175;
end;

function TfrmChartTest.GetData: TFDMemTable;
var
  Data: TFDMemTable;
begin
  Data := TFDMemTable.Create(nil);
  CreateField(Data, dateField, ftDate, 0);
  CreateField(Data, intField1, ftInteger, 0);
  CreateField(Data, intField2, ftInteger, 0);
  Data.CreateDataSet();
  Data.Append();
  Data.FieldByName(dateField).AsDateTime := Yesterday;
  Data.FieldByName(intField1).AsInteger := 50;
  Data.FieldByName(intField2).AsInteger := 130;
  Data.Append();
  Data.FieldByName(dateField).AsDateTime := Today;
  Data.FieldByName(intField1).AsInteger := 70;
  Data.FieldByName(intField2).AsInteger := 150;
  Data.Append();
  Data.FieldByName(dateField).AsDateTime := Tomorrow;
  Data.FieldByName(intField1).AsInteger := 90;
  Data.FieldByName(intField2).AsInteger := 170;
  Exit(Data);
end;

end.

No values from the Y-axe are displayed. Values on X-axe are displayed. No error messages are shown, it's just not working as intended.


Solution

  • The only functional problem with your code is that you create an instance of abstract TChartSeries. You didn't mention what kind of series are you trying to create at run-time. To fix this just pick a specific series like TLineSeries or TBarSeries and your code should work.

    Besides that the way you create series at runtime is a bit awkward. You don't need to call AddSeries after you set property ParentChart of series. Those two are basically equivalent. The simplest way to create parented series is described in the documentation and it can be a one-liner (this one creates bar series):

    chartSeries := chtTestChart.AddSeries(TBarSeries.Create(Self));
    { setup series here }
    

    You also don't need to set property Active of series to True, because it's the default value.