Introduction
On occasion you may wish to create a calculated or lookup field dynamically.
If you've thought of this, you may have been put off by the statements in the
C++ Builder help:
Do not create instances of TField.
This seems fairly unambiguous, but it is wrong.
The Steps
Create your form, data modules, and tables. The table for which you are going
to dynamically create lookup and / or calculated fields should either have persistent
fields created at design-time, or programatically prior to adding the lookup
/ calculated fields. The table should be inactive at the time you add the lookup
/ calculated fields, and can be made active afterward. If you are adding a calculated
field, you must have an OnCalcFields handler to calculate the value. This can
be established for the table at design-time or assigned to the OnCalcFields
event at run-time. In any event, the code must be present for this at design
time (though you could, with some complication, make this dynamic by calling
functions from a dynamically loaded DLL). Then make the table active, and you
are set.
The Example
The example adds a calculated and lookup field to a table at run time. The
calculated field essentially duplicates the lookup field for simplicity, but
of course could contain any calculated value.
The tables are a table of accounts which have a zone ID foreign key field and
a Zone domain table which contains the description of the zone. Two copies of
this table are used - one for the lookup and one for the calculated field. This
may not be strictly necessary, but keeps things simple.
This is what it looks like at run-time:

Here is the .dfm
object Form1: TForm1
Left = 3
Top = 155
Width = 696
Height = 216
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 120
TextHeight = 16
object DBGrid1: TDBGrid
Left = 8
Top = 16
Width = 673
Height = 161
DataSource = DataSource1
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -13
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = []
end
object AccountTable: TTable
OnCalcFields = AccountTableCalcFields
DatabaseName = 'PVMapping'
Filter = 'ZONE_ID<>'#39#39
Filtered = True
TableName = 'Account.DBF'
Left = 624
Top = 16
object AccountTableID: TStringField
FieldName = 'ID'
end
object AccountTableZONE_ID: TStringField
FieldName = 'ZONE_ID'
Size = 25
end
end
object DataSource1: TDataSource
DataSet = AccountTable
Left = 624
Top = 48
end
object ZoneLookupFieldTable: TTable
Active = True
DatabaseName = 'PVMapping'
TableName = 'Zone.dbf'
Left = 624
Top = 136
end
object ZoneCalculatedFieldTable: TTable
Active = True
DatabaseName = 'PVMapping'
TableName = 'Zone.dbf'
Left = 624
Top = 184
end
end
The Code
The code itself is simple:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MainFormUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
TStringField *CalcField = new TStringField(this);
CalcField->FieldName = "TestDynamicCalc";
CalcField->FieldKind = fkCalculated;
CalcField->DataSet = AccountTable;
CalcField->Name = "Table1TestDynamicCalc";
TStringField *LookupField = new TStringField(this);
LookupField->FieldName = "TestDynamicLookup";
LookupField->FieldKind = fkLookup;
LookupField->DataSet = AccountTable;
LookupField->LookupDataSet = ZoneLookupFieldTable;
LookupField->KeyFields = "ZONE_ID";
LookupField->LookupKeyFields = "ID";
LookupField->LookupResultField = "DESC";
LookupField->Name = "Table1TestDynamicLookup";
AccountTable->Active = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::AccountTableCalcFields(TDataSet *DataSet)
{
if
(
ZoneCalculatedFieldTable->Locate
(
"ID",
AccountTable->FieldByName("ZONE_ID")->AsString,
TLocateOptions()
)
)
{
AccountTable->FieldByName("TestDynamicCalc")->AsString
=
ZoneCalculatedFieldTable->FieldByName("DESC")->AsString;
};
}
//---------------------------------------------------------------------------
Conclusion
Contrary to common belief, it is not hard to create calculated
and lookup fields for tables and queries at run-time. Whether it is worth the
effort depends on your application.
|