FireDAC VCL Component: Creating a Data Source at Runtime

Introduction

To assign a data source to the FireDAC VCL component of List & Label in the Embarcadero RAD Studio, you typically use the built-in dialog for the DataController > DetailSources property of the List & Label component:

The decisive factor here is that the data source must already be known and configured at design time, i.e. when the application is created. But how does this work dynamically at runtime, so that you don’t have to use the dialog at design time of the application, for example, in order to provide a degree of flexibility for the later users of the application? This is exactly the aspect we will show in a simplified form below.

Data Source Dynamically in Runtime Code

To do this, we use two TDataSource objects to map the two simple tables Customers and Orders. In addition, a relational structure between these two tables will be defined later to map a master-detail relationship.

Define Customers and Orders and Fill Them With Data

Using TDataSource objects, the data structure is defined and simultaneously filled with data:

procedure GetDataSource(var DataSourceCustomers, DataSourceOrders: TDataSource);
var

  DataSetCutomers: TClientDataSet;
  DataSetOrders: TClientDataSet;

begin

  {* Create DataSet/DataSource for "Customers" with Data *}
  DataSetCutomers := TClientDataSet.Create(nil);
  with DataSetCutomers do
  begin

    Name := 'Customers';
    FieldDefs.Add('Id', ftInteger);
    FieldDefs.Add('Name', ftString, 255);
    CreateDataSet;

    IndexDefs.Add('PK_Test', 'Id', [ixPrimary, ixUnique]);

    Append;
    FieldByName('Id').AsInteger := 1;
    FieldByName('Name').AsString := 'Lars';

    Append;
    FieldByName('Id').AsInteger := 2;
    FieldByName('Name').AsString := 'Dirk';

    Append;
    FieldByName('Id').AsInteger := 3;
    FieldByName('Name').AsString := 'Torsten';

    Post;

  end;

  DataSourceCustomers := TDataSource.Create(nil);
  DataSourceCustomers.DataSet := DataSetCutomers;

  {* Create DataSet/DataSource for "Orders" with Data and
  define its relation to "Customers" *}
  DataSetOrders := TClientDataSet.Create(nil);
  with DataSetOrders do
  begin

    Name := 'Orders';
    FieldDefs.Add('Id', ftInteger);
    FieldDefs.Add('CustomerID', ftInteger);
    FieldDefs.Add('Amount', ftFloat);
    CreateDataSet;

    IndexDefs.Add('PK_InnerTable', 'Id', [ixPrimary, ixUnique]);
    IndexFieldNames := 'CustomerID';

    Append;
    FieldByName('Id').AsInteger := 1;
    FieldByName('CustomerID').AsInteger := 1;
    FieldByName('Amount').AsFloat := 99.25;

    Append;
    FieldByName('Id').AsInteger := 2;
    FieldByName('CustomerID').AsInteger := 1;
    FieldByName('Amount').AsFloat := 125.99;

    Append;
    FieldByName('Id').AsInteger := 3;
    FieldByName('CustomerID').AsInteger := 2;
    FieldByName('Amount').AsFloat := 12.98;

    Post;

  end;

  DataSourceOrders := TDataSource.Create(nil);
  DataSourceOrders.DataSet := DataSetOrders;

  DataSetOrders.MasterSource := DataSourceCustomers;
  DataSetOrders.MasterFields := 'Id';

end;

Assign Created Data Source to the List & Label Object

Once the structure has been created with the data, it can be assigned to the List & Label object:

  {* Create the List & Label object and assign the DataSource *}
  LL := TListLabel25.Create(self);
  GetDataSource(DataSourceCustomers, DataSourceOrders);
  LL.DataController.DataSource := DataSourceCustomers;

Define Relation in the Data Source

Then the relation must be defined in the DataController, which is then to be used by List & Label in the Designer and during printing:

  {* Prepare the relational structure between the DataSources within
  the List & Label DataController *}
  with LL.DataController.DetailSources.Add do
  begin

    Name := 'Customers';
    DataSource := DataSourceCustomers;
    PrimaryKeyField := 'Id';

    with TDetailSourceItem(AddChildNode) do
    begin

      Name := 'Orders';
      DataSource := DataSourceOrders;
      MasterKeyField := 'Id';
      DetailKeyField := 'CustomerID';

    end;

  end;

Start Designer and Verify Data Sources

Now you can simply call the Design function to check the result of the provided data sources and create the report:

  // Start the Designer
  LL.Design();

grafik

Conclusion

This makes it quick and easy to provide the later user of the application with a quite flexible approach to data design in reporting.