{*********************************************}
{  TeeChart LiveBindings DBLink               }
{  Copyright (c) 2012 by Steema Software      }
{  All Rights Reserved                        }
{*********************************************}
unit VCLTee.Bind.DBLinks;
{$I TeeDefs.inc}

interface

{$IFDEF FMX}
{$HPPEMIT '#pragma link "FMXTee.Bind.DBLinks"'}    {Do not Localize}
{$ELSE}
{$HPPEMIT '#pragma link "VCLTee.Bind.DBLinks"'}    {Do not Localize}
{$ENDIF}

uses
  System.Classes, System.Generics.Collections, Data.DB, Data.Bind.DBLinks,
  Data.Bind.Components,
  {$IFDEF FMX}
  Fmx.Bind.DBLinks, FMXTee.Chart, FMXTee.Engine
  {$ELSE}
  VCLTee.Chart, VCLTee.TeEngine
  {$ENDIF}
  ;

type
  {$IFNDEF FMX}
  TDBGridLinkColumn=class(TBaseDBGridLinkColumn);
  TDBGridLinkColumns=class(TBaseDBGridLinkColumns);
  {$ENDIF}

  TCustomBindDBChartLink = class(TBaseBindDBGridLink)
  private
    FColumnDescriptionsLookup: TDictionary<TObject, Integer>;
    FColumnDescriptions: TList<TDBGridLinkColumnDescription>;
    FColumns: TDBGridLinkColumns;
    procedure CreateDefaultGridColumns;
    procedure CreateCollectionGridColumns;
     procedure SetColumns(Value: TDBGridLinkColumns);
    function ColumnsHaveChanged: Boolean;
    function CreateDefaultGridColumnDescriptions: TArray<TDBGridLinkColumnDescription>;
    function CreateCollectionGridColumnDescriptions: TArray<TDBGridLinkColumnDescription>;
    function CreateGridColumnDescriptions: TArray<TDBGridLinkColumnDescription>;
    function GetChart:TCustomChart;
    procedure SetChart(const Value:TCustomChart);
  protected
    function GetColumns: TBaseDBGridLinkColumns; override;
    procedure InvalidateColumn(AColumn: TBaseDBGridLinkColumn); override;
    procedure InvalidateField(AField: TField); override;
    procedure ClearColumns(AManager: IBindDBGridLinkControlManager); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure AddSeries(ASeries:TChartSeries; const AFieldName:String); overload;
    procedure AddSeries(ASeries:TChartSeries; AField:TField); overload;

    procedure ClearGeneratedExpressions{$IFDEF D17}(Sender: TComponent){$ENDIF}; override;
    procedure CreateGridColumns;
    procedure GenerateExpressions{$IFDEF D17}(Sender: TComponent){$ENDIF}; override;
    function RequiresControlHandler: Boolean; override;
    procedure UpdateColumns; override;

    property Chart: TCustomChart read GetChart write SetChart;
    property Columns: TDBGridLinkColumns read FColumns write SetColumns;
  end;

  TBindDBChartLink = class(TCustomBindDBChartLink)
  published
    property DataSource;
    property Chart;
    property AutoActivate;
    property BufferCount;
    property Columns;
    property OnAssignedValue;
    property OnAssigningValue;
    property OnEvalError;
    property OnActivating;
    property OnActivated;
  end;

implementation

uses
  System.SysUtils,
  {$IFDEF FMX}
  FMX.Types, FMXTee.Series, FMXTee.Bind.Editors, FMXTee.DBChart
  {$ELSE}
  VCLTee.Series, VCLTee.Bind.Editors, VCLTee.DBChart
  {$ENDIF}
  ;

type
  TCreateSeriesDescription = record
  private
    FField: TField;
    FHeader: String;
    FFieldName: String;
    FSeries: TChartSeries;
    FSeriesClass: TChartSeriesClass;
    FVisible: Boolean;

    procedure SetSeries(const Value:TChartSeries);
  public
    constructor Create(AField: TField); overload;

    constructor Create(AColumn: TDBGridLinkColumn); overload;

    property Field: TField read FField;
    property FieldName: String read FFieldName;
    property Header: String read FHeader;
    property Series:TChartSeries read FSeries write SetSeries;
    property SeriesClass:TChartSeriesClass read FSeriesClass write FSeriesClass;
    property Visible: Boolean read FVisible;
  end;

  TBindDBChartColumnCreator = class(TInterfacedObject, IBindDBGridLinkControlManager)
  private
    FChart: TCustomChart;
    function CreateSeriesDescription(
      AIndex: Integer; ADescription: TCreateSeriesDescription): TDBGridLinkColumnDescription;
    function AddColumn(
      const ADescription: TCreateSeriesDescription): Integer; overload;
    procedure UpdateColumn(const ACreateDescription: TCreateSeriesDescription; const ADescription: TDBGridLinkColumnDescription); overload;
    function DescribeColumn(
      AIndex: Integer; const ADescription: TCreateSeriesDescription): TDBGridLinkColumnDescription; overload;
  public
    constructor Create(AChart: TCustomChart);
    procedure BeginUpdate;
    procedure EndUpdate;
    function CanAddColumn(AField: TField): Boolean; overload;
    function CanAddColumn(AColumn: TBaseDBGridLinkColumn): Boolean; overload;
    procedure ClearColumns;
    function DescribeColumn(AIndex: Integer; AColumn: TBaseDBGridLinkColumn): TDBGridLinkColumnDescription; overload;
    function DescribeColumn(AIndex: Integer; AField: TField): TDBGridLinkColumnDescription; overload;
    function AddColumn(AColumn: TBaseDBGridLinkColumn): Integer; overload;
    function AddColumn(AField: TField): Integer; overload;
    procedure UpdateColumn(AColumn: TBaseDBGridLinkColumn; const ADescription: TDBGridLinkColumnDescription); overload;
    procedure UpdateColumn(AField: TField; const ADescription: TDBGridLinkColumnDescription); overload;
  end;

  TBindDBChartColumnFactory = class(TBindDBColumnFactory)
  public
    constructor Create; override;
    function Supports(AIntf: TGuid; AGrid: TComponent): Boolean; override;
    function CreateFactory(AIntf: TGuid; AGrid: TComponent): IInterface; override;
  end;

{$IFDEF TEELITE}
Function TeeFieldType(AType:TFieldType):TTeeFieldType; forward;
{$ENDIF}

{ TBindDBChartColumnCreator }

function TBindDBChartColumnCreator.AddColumn(
  AField: TField): Integer;
begin
  Result := AddColumn(TCreateSeriesDescription.Create(AField));
end;

procedure TBindDBChartColumnCreator.BeginUpdate;
begin
  {$IFDEF FMX}
  FChart.BeginUpdate;
  {$ENDIF}
end;

function TBindDBChartColumnCreator.DescribeColumn(
  AIndex: Integer; AColumn: TBaseDBGridLinkColumn): TDBGridLinkColumnDescription;
begin
  Result := DescribeColumn(AIndex, TCreateSeriesDescription.Create(AColumn as TDBGridLinkColumn));
end;

function TBindDBChartColumnCreator.DescribeColumn(
  AIndex: Integer; AField: TField): TDBGridLinkColumnDescription;
begin
  Result := DescribeColumn(AIndex, TCreateSeriesDescription.Create(AField));
end;

function TBindDBChartColumnCreator.AddColumn(
  AColumn: TBaseDBGridLinkColumn): Integer;
begin
  Result := AddColumn(TCreateSeriesDescription.Create(AColumn as TDBGridLinkColumn));
end;

procedure TBindDBChartColumnCreator.UpdateColumn(
  AField: TField; const ADescription: TDBGridLinkColumnDescription);
var
  AUpdateSeriesDescription: TCreateSeriesDescription;
begin
  AUpdateSeriesDescription := TCreateSeriesDescription.Create(AField);
  UpdateColumn(AUpdateSeriesDescription, ADescription);
end;

procedure TBindDBChartColumnCreator.UpdateColumn(
  AColumn: TBaseDBGridLinkColumn; const ADescription: TDBGridLinkColumnDescription);
begin
  UpdateColumn(TCreateSeriesDescription.Create(AColumn as TDBGridLinkColumn),
    ADescription);
end;

type
  TSeriesAccess=class(TChartSeries);

function TBindDBChartColumnCreator.AddColumn(
  const ADescription: TCreateSeriesDescription): Integer;
var
  Series: TChartSeries;
begin
  Series:=ADescription.FSeries;

  if not Assigned(Series) then
  begin
    if Assigned(ADescription.SeriesClass) then
       Series:=ADescription.SeriesClass.Create(FChart)
    else
      Series:=GuessBestSeries(ADescription.Field).Create(FChart);

    TSeriesAccess(Series).DontSerialize:=True;
  end;

  Series.Title:=ADescription.Header;
  Series.Visible := ADescription.Visible;

  if Series.ParentChart<>FChart then
     FChart.AddSeries(Series);

  Result := Series.SeriesIndex;
end;

function TBindDBChartColumnCreator.DescribeColumn(
  AIndex: Integer; const ADescription: TCreateSeriesDescription): TDBGridLinkColumnDescription;
begin
  Result := CreateSeriesDescription(AIndex, ADescription);
end;

procedure TBindDBChartColumnCreator.UpdateColumn(
  const ACreateDescription: TCreateSeriesDescription; const ADescription: TDBGridLinkColumnDescription);
var
  LColumn: TChartSeries;
begin
  LColumn := ADescription.ColumnControl as TChartSeries;
  if LColumn <> nil then
  begin
    LColumn.Title := ACreateDescription.Header;
    LColumn.Visible := ACreateDescription.Visible;
  end;
end;

function TBindDBChartColumnCreator.CreateSeriesDescription(
  AIndex: Integer; ADescription: TCreateSeriesDescription): TDBGridLinkColumnDescription;
var
  LColumn: TDBGridLinkColumnDescription;
  FPairsList: TList<TDBGridLinkColumnExpressionPair>;
  LPair: TDBGridLinkColumnExpressionPair;
  LFormatColumnExpressions: TArray<TDBGridLinkColumnExpressionPair>;
  LFormatCellExpressions: TArray<TDBGridLinkColumnExpressionPair>;
  LParseCellExpressions: TArray<TDBGridLinkColumnExpressionPair>;
  LCellExpression: String;
  LField: TField;
  t, SeriesIndex : Integer;
begin
  LField := ADescription.Field;
  FPairsList := TList<TDBGridLinkColumnExpressionPair>.Create;
  try
    FPairsList.Clear;
    LFormatColumnExpressions := FPairsList.ToArray;

    if LField = nil then
      LCellExpression := ''
    else if LField.IsBlob then
      LCellExpression := '''(blob)'''
    else
      LCellExpression := 'DisplayText';

    SeriesIndex:=-1;

    for t:=0 to AIndex do
    begin
      Inc(SeriesIndex);

      while not TSeriesAccess(FChart[SeriesIndex]).DontSerialize do
         Inc(SeriesIndex);
    end;

    FPairsList.Clear;
    if LCellExpression <> '' then
    begin
      LPair := TDBGridLinkColumnExpressionPair.Create(
        Format('Series[%d]', [SeriesIndex]), LCellExpression);
      FPairsList.Add(LPair);
      LFormatCellExpressions := FPairsList.ToArray;
    end;

    if LField = nil then
      LCellExpression := ''
    else if LField.IsBlob then
      LCellExpression := ''
    else
      LCellExpression := 'Text';

    FPairsList.Clear;
    if LCellExpression <> '' then
    begin
      LPair := TDBGridLinkColumnExpressionPair.Create(
      'SelectedText(Self)', LCellExpression);
      FPairsList.Add(LPair);
    end;
    LParseCellExpressions := FPairsList.ToArray;

    LColumn := TDBGridLinkColumnDescription.Create(FChart[SeriesIndex], '',
     AIndex, '', ADescription.FieldName,
     LFormatColumnExpressions, LFormatCellExpressions, LParseCellExpressions);

    Result := LColumn;
  finally
    FPairsList.Free;
  end;
end;

procedure TBindDBChartColumnCreator.EndUpdate;
begin
  {$IFDEF FMX}
  FChart.EndUpdate;
  FChart.Invalidate;
  {$ENDIF}
end;

function TBindDBChartColumnCreator.CanAddColumn(AField: TField): Boolean;
begin
  Result := Assigned(AField) and (TeeFieldType(AField.DataType)=tftNumber);
end;

function TBindDBChartColumnCreator.CanAddColumn(
  AColumn: TBaseDBGridLinkColumn): Boolean;
begin
  Result := CanAddColumn(AColumn.Field);
end;

procedure TBindDBChartColumnCreator.ClearColumns;
var t : Integer;
begin
  t:=0;

  while t<FChart.SeriesCount do
    if TSeriesAccess(FChart[t]).DontSerialize then
       {$IFDEF AUTOREFCOUNT}
       FChart[t].DisposeOf
       {$ELSE}
       FChart[t].Free
       {$ENDIF}
    else
       Inc(t);
end;

constructor TBindDBChartColumnCreator.Create(AChart: TCustomChart);
begin
  FChart := AChart;
end;

{ TBindDBChartColumnFactory }

constructor TBindDBChartColumnFactory.Create;
begin
  inherited;
end;

function TBindDBChartColumnFactory.CreateFactory(AIntf: TGuid;
  AGrid: TComponent): IInterface;
begin
  Result := TBindDBChartColumnCreator.Create(TCustomChart(AGrid));
end;

function TBindDBChartColumnFactory.Supports(AIntf: TGuid;
  AGrid: TComponent): Boolean;
begin
  Result:= (AIntf = IBindDBGridLinkControlManager) and Assigned(AGrid) and
           AGrid.InheritsFrom(TCustomChart);
end;

{$IFDEF TEELITE}
Function TeeFieldType(AType:TFieldType):TTeeFieldType;
begin
  Case AType of
    ftAutoInc,
    ftCurrency,
    ftFloat,
    ftInteger,
    ftLargeInt,
    ftSmallint,
    ftWord,

    {$IFDEF D12}
    ftLongWord,
    ftShortint,
    ftByte,
    TFieldType.ftExtended,
    {$ENDIF}

    {$IFDEF D14}
    TFieldType.ftSingle,
    {$ENDIF}

    {$IFDEF D6}
    ftFMTBcd,
    {$ENDIF}

    ftBCD        : result:=tftNumber;

    ftDate,
    ftTime,

    {$IFDEF D7}
    ftTimeStamp,
    {$ENDIF}

    {$IFDEF D10}
    ftOraTimeStamp,
    {$ENDIF}

    ftDateTime   : result:=tftDateTime;

    ftString,
    ftFixedChar,

    ftMemo,
    ftFmtMemo,

    {$IFDEF D10}
    ftFixedWideChar,
    ftWideMemo,
    {$ENDIF}

    ftWideString : result:=tftText;
  else
    result:=tftNone;
  end;
end;
{$ENDIF}

{ TCustomBindDBChartLink }

function TCustomBindDBChartLink.GetChart: TCustomChart;
begin
  if Assigned(BindGridLink.ControlComponent) and (BindGridLink.ControlComponent is TCustomChart) then
     Result := TCustomChart(BindGridLink.ControlComponent)
  else
     Result := nil;
end;

procedure TCustomBindDBChartLink.SetChart(const Value: TCustomChart);
begin
  BindGridLink.ControlComponent := Value;
end;

{ TCustomBindDBChartLink }

procedure TCustomBindDBChartLink.CreateGridColumns;
begin
  if Columns.Count = 0 then
     CreateDefaultGridColumns
  else
     CreateCollectionGridColumns;
end;

function TCustomBindDBChartLink.CreateGridColumnDescriptions: TArray<TDBGridLinkColumnDescription>;
begin
  if Columns.Count = 0 then
     Result := CreateDefaultGridColumnDescriptions
  else
     Result := CreateCollectionGridColumnDescriptions;
end;

procedure TCustomBindDBChartLink.ClearGeneratedExpressions{$IFDEF D17}(Sender: TComponent){$ENDIF};
var
  LManager: IBindDBGridLinkControlManager;
begin
  inherited;

  if Columns.Count = 0 then
  begin
    // Clear default columns
    LManager := GetBindDBColumnManager;

    if LManager <> nil then
       ClearColumns(LManager);
  end;

  FColumnDescriptions.Clear;
end;

function TCustomBindDBChartLink.ColumnsHaveChanged: Boolean;
var
  LDescriptions: TArray<TDBGridLinkColumnDescription>;
  I: Integer;
begin
  LDescriptions := CreateGridColumnDescriptions;
  Result := FColumnDescriptions.Count <> Length(LDescriptions);
  if not Result then
  begin
    for I := 0 to FColumnDescriptions.Count - 1 do
      if not FColumnDescriptions[I].IsEqual(LDescriptions[I]) then
        Exit(True);
  end;
end;

destructor TCustomBindDBChartLink.Destroy;
var
  LManager: IBindDBGridLinkControlManager;
begin
  Active := False;
  FColumnDescriptionsLookup.Free;
  FColumnDescriptions.Free;
  FColumns.Free;
  LManager := GetBindDBColumnManager;

  if LManager <> nil then
     ClearColumns(LManager);

  inherited;
end;

constructor TCustomBindDBChartLink.Create(AOwner: TComponent);
begin
  inherited;
  FColumnDescriptionsLookup := TDictionary<TObject, Integer>.Create;
  FColumnDescriptions := TList<TDBGridLinkColumnDescription>.Create;
  FColumns := TDBGridLinkColumns.Create(Self, TDBGridLinkColumn);
end;

procedure TCustomBindDBChartLink.CreateCollectionGridColumns;
var
  LManager: IBindDBGridLinkControlManager;
  I: Integer;
  LColumn: TBaseDBGridLinkColumn;
  LIndex: Integer;
begin
  LManager := GetBindDBColumnManager;

  if LManager <> nil then
  begin
    LManager.BeginUpdate;
    try
      ClearColumns(LManager);

      LIndex := 0;
      for I := 0 to Columns.Count - 1 do
      begin
        LColumn := {$IFDEF FMX}Columns[I]{$ELSE}Columns.BaseItems[I]{$ENDIF};
        begin
          if ((LColumn.Field = nil) or LColumn.Field.Visible) and LManager.CanAddColumn(LColumn) then
          begin
            LManager.AddColumn(LColumn);
            FColumnDescriptionsLookup.Add(LColumn, LIndex);
            Inc(LIndex);
          end;
        end;
      end;
    finally
      LManager.EndUpdate;
    end;
  end;
end;

function TCustomBindDBChartLink.CreateCollectionGridColumnDescriptions: TArray<TDBGridLinkColumnDescription>;
var
  LManager: IBindDBGridLinkControlManager;
  LColumnDescription: TDBGridLinkColumnDescription;
  I: Integer;
  LColumn: TBaseDBGridLinkColumn;
  LList: TList<TDBGridLinkColumnDescription>;
begin
  LList := TList< TDBGridLinkColumnDescription>.Create;
  try
  LManager := GetBindDBColumnManager;
  if LManager <> nil then
  begin
      for I := 0 to Columns.Count - 1 do
      begin
        LColumn := {$IFDEF FMX}Columns[I]{$ELSE}Columns.BaseItems[I]{$ENDIF};
        begin
          if ((LColumn.Field = nil) or LColumn.Field.Visible) and LManager.CanAddColumn(LColumn) then
          begin
            LColumnDescription := LManager.DescribeColumn(LList.Count, LColumn);
            LList.Add(LColumnDescription);
          end;
        end;
      end;
  end;
  Result := LList.ToArray;
  finally
    LList.Free;
  end;
end;

procedure TCustomBindDBChartLink.AddSeries(ASeries: TChartSeries;
  AField: TField);
begin
  Columns.Add.Field:=AField;
  Chart:=ASeries.ParentChart as TCustomChart;
  UpdateColumns;
end;

procedure TCustomBindDBChartLink.AddSeries(ASeries: TChartSeries;
  const AFieldName: String);
begin
  AddSeries(ASeries, DataSource.DataSource.DataSet.FieldByName(AFieldName));
end;

procedure TCustomBindDBChartLink.ClearColumns(AManager:  IBindDBGridLinkControlManager);
begin
  inherited;
  FColumnDescriptionsLookup.Clear;
end;

procedure TCustomBindDBChartLink.CreateDefaultGridColumns;
var
  LField: TField;
  LDataSet: TDataSet;
  LManager: IBindDBGridLinkControlManager;
  LIndex: Integer;
begin
  LManager := GetBindDBColumnManager;
  if LManager <> nil then
  begin
    LManager.BeginUpdate;
    try
      ClearColumns(LManager);
      LDataSet := GetDataSet;
      LIndex := 0;
      if LDataSet <> nil then
        for LField in LDataSet.Fields do
        begin
          if LField.Visible and LManager.CanAddColumn(LField) then
          begin
            LManager.AddColumn(LField);
            FColumnDescriptionsLookup.Add(LField, LIndex);
            Inc(LIndex);
          end;
        end;
    finally
      LManager.EndUpdate;
    end;
  end;
end;


function TCustomBindDBChartLink.CreateDefaultGridColumnDescriptions: TArray<TDBGridLinkColumnDescription>;
var
  LField: TField;
  LDataSet: TDataSet;
  LManager: IBindDBGridLinkControlManager;
  LColumnDescription: TDBGridLinkColumnDescription;
  LList: TList<TDBGridLinkColumnDescription>;
begin
  LList := TList<TDBGridLinkColumnDescription>.Create;
  try
  LManager := GetBindDBColumnManager;
  if LManager <> nil then
  begin
      LDataSet := GetDataSet;
      if LDataSet <> nil then
        for LField in LDataSet.Fields do
        begin
          if LField.Visible and LManager.CanAddColumn(LField) then
          begin
            LColumnDescription := LManager.DescribeColumn(LList.Count, LField);
            LList.Add(LColumnDescription);
          end;
        end;
  end;
  Result := LList.ToArray;
  finally
    LList.Free;
  end;
end;


procedure TCustomBindDBChartLink.UpdateColumns;
var
  LPair: TPair<TObject, Integer>;
begin
  if csDestroying in ComponentState then
     Exit;

  if (FColumnDescriptions.Count = 0) or ColumnsHaveChanged then
  begin
    CreateGridColumns;
    ClearGeneratedExpressions{$IFDEF D17}(Self){$ENDIF};

    if Active then
      GenerateExpressions{$IFDEF D17}(Self){$ENDIF};
  end
  else
  begin
    // Expressions haven't changed but column heading etc. may need updating
    for LPair in FColumnDescriptionsLookup do
      if LPair.Key is TField then
        InvalidateField(TField(LPair.Key))
      else if LPair.Key is TDBGridLinkColumn then
        InvalidateColumn(TDBGridLinkColumn(LPair.Key));
  end;

end;


procedure TCustomBindDBChartLink.GenerateExpressions{$IFDEF D17}(Sender: TComponent){$ENDIF};
var
  LItem: TExpressionItem;
  LColumn: TDBGridLinkColumnDescription;
  LColumnExpressionItem: TColumnLinkExpressionItem;
  LPair: TDBGridLinkColumnExpressionPair;
  LDescriptions: TArray<TDBGridLinkColumnDescription>;

begin

  if Columns.Count = 0 then
    if FColumnDescriptionsLookup.Count = 0 then
      // Generate default columns
      CreateDefaultGridColumns;

  Assert(BindGridLink.ColumnExpressions.Count = 0);
  Assert(BindGridLink.FormatControlExpressions.Count = 0);
  Assert(BindGridLink.ClearControlExpressions.Count = 0);
  Assert(BindGridLink.PosSourceExpressions.Count = 0);
  Assert(BindGridLink.PosControlExpressions.Count = 0);

  LDescriptions := CreateGridColumnDescriptions;
  Assert(FColumnDescriptions.Count = 0);
  for LColumn in LDescriptions do
  begin
    FColumnDescriptions.Add(LColumn);
    LColumnExpressionItem := BindGridLink.ColumnExpressions.AddExpression;
    LColumnExpressionItem.ColumnName := LColumn.ColumnName;
    LColumnExpressionItem.Name := ''; // is this needed?
    LColumnExpressionItem.ColumnIndex := LColumn.ColumnIndex;
    LColumnExpressionItem.SourceMemberName := LColumn.SourceMemberName;
    LColumnExpressionItem.ControlMemberName := LColumn.ControlMemberName;
    {
    for LPair in LColumn.ParseCellExpression do
    begin
      LItem := LColumnExpressionItem.ParseCellExpressions.AddExpression;
      LItem.ControlExpression := LPair.ControlExpression;
      LItem.SourceExpression := LPair.SourceExpression;
    end;
    }
    for LPair in LColumn.FormatCellExpressions do
    begin
      LItem := LColumnExpressionItem.FormatCellExpressions.AddExpression;
      LItem.ControlExpression := LPair.ControlExpression;
      LItem.SourceExpression := LPair.SourceExpression;
    end;

    for LPair in LColumn.FormatColumnExpressions do
    begin
      LItem := LColumnExpressionItem.FormatColumnExpressions.AddExpression;
      LItem.ControlExpression := LPair.ControlExpression;
      LItem.SourceExpression := LPair.SourceExpression;
    end;

  end;

  {
  LItem := BindGridLink.PosControlExpressions.AddExpression;
  LItem.ControlExpression := 'Selected';
  LItem.SourceExpression := 'RecNo-1';

  LItem := BindGridLink.PosSourceExpressions.AddExpression;
  LItem.ControlExpression := 'Selected+1';
  LItem.SourceExpression := 'RecNo';
  }
end;


function TCustomBindDBChartLink.GetColumns: TBaseDBGridLinkColumns;
begin
  Result := FColumns;
end;

procedure TCustomBindDBChartLink.InvalidateColumn(
  AColumn: TBaseDBGridLinkColumn);
var
  LManager: IBindDBGridLinkControlManager;
  LIndex: Integer;
  LDescription: TDBGridLinkColumnDescription;
begin
  if FColumnDescriptionsLookup.TryGetValue(AColumn, LIndex) then
  begin
    if FColumnDescriptions.Count > LIndex then
    begin
      LDescription := FColumnDescriptions[LIndex];
      LManager := GetBindDBColumnManager;
      if LManager <> nil then
        LManager.UpdateColumn(AColumn, LDescription)
      else
        inherited;
    end;
  end;

end;

procedure TCustomBindDBChartLink.InvalidateField(
  AField: TField);
var
  LManager: IBindDBGridLinkControlManager;
  LIndex: Integer;
  LDescription: TDBGridLinkColumnDescription;
begin
  if FColumnDescriptionsLookup.TryGetValue(AField, LIndex) then
  begin
    if FColumnDescriptions.Count > LIndex then
    begin
      LDescription := FColumnDescriptions[LIndex];
      LManager := GetBindDBColumnManager;
      if LManager <> nil then
        LManager.UpdateColumn(AField, LDescription)
      else
        inherited;
    end;
  end;

end;

function TCustomBindDBChartLink.RequiresControlHandler: Boolean;
begin
  result:=False;
end;

procedure TCustomBindDBChartLink.SetColumns(Value: TDBGridLinkColumns);
begin
  FColumns.Assign(Value);
end;

{ TCreateSeriesDescription }

constructor TCreateSeriesDescription.Create(AField: TField);
begin
  FField := AField;
  FHeader := FField.DisplayName;
  FFieldName := FField.FieldName;
  FVisible := FField.Visible;
end;

constructor TCreateSeriesDescription.Create(AColumn: TDBGridLinkColumn);
begin
  FField := AColumn.Field;
  FFieldName := AColumn.FieldName;

  {$IFDEF FMX}
  FHeader := AColumn.Header;
  FVisible := AColumn.Visible;
  {$ELSE}
  FHeader := FField.DisplayLabel;
  FVisible := FField.Visible;
  {$ENDIF}
end;

procedure TCreateSeriesDescription.SetSeries(const Value:TChartSeries);
begin
  FSeries:=Value; // FreeNotification ?
end;

initialization
  RegisterBindDBColumnFactory([TBindDBChartColumnFactory]);
  RegisterTeeStandardSeries;
finalization
  UnRegisterBindDBColumnFactory([TBindDBChartColumnFactory]);
end.
