Skip to content

TTMSFNCDataBinding

To get started with the databinding component, drop a TTMSFNCDataBinder (further referred to as databinder) component on the form. The databinder component is capable of binding single value components, lists, tables and grids as well as non published properties such as generic TList & TObjectList (Integer or String based). It also automatically updates when a dataset change occurs.

The chapters below demonstrate what is possible based on a TClientDataSet connected to the biolife.xml file provided by Embarcadero, a TDataSource and a single instance of TTMSFNCDataBinder.

Binding a single value

Binding a single value can be done by adding an item to the databinder component, specifying the component, the field name and the property name. It shows the value based on the active record. There are several ways that this can be done.

Programmatically

procedure TFormDataBinding.UpdateLinks;
var
  it: TTMSFNCDataBinderItem;
begin
  TMSFNCDataBinder1.BeginUpdate;
  it := TMSFNCDataBinder1.Items.Add;
  it.&Object := TMSFNCHTMLText1;
  it.BindType := dbbtSingleValue;
  it.DataSource := DataSource1;
  it.FieldName := 'Common_Name';
  it.PropertyName := 'Text';
  TMSFNCDataBinder1.EndUpdate;
  TMSFNCDataBinder1.Active := True;
end;
The below code actually sets up the same binding as the code above with a convenience method ConnectSingle.
procedure TFormDataBinding.UpdateLinks;
begin
  TMSFNCDataBinder1.ConnectSingle(TMSFNCHTMLText1, DataSource1, 'Text', 'Common_Name');
  TMSFNCDataBinder1.Active := True;
end;

Designtime

The databinder component also registered designtime support retrieving components, fieldnames and properties and makes this easily selectable at designtime via the object inspector. Add an item to the Items collection inside the TTMSFNCDataBinder component, select the item in the object inspector and select the values necessary to bind a single value to the component as demonstrated below.

TMSFNCDataBinding

Result:

TMSFNCDataBinding1

TMSFNCDataBinding2

Binding a list (TStringList)

Binding a list is similar to binding a single value but it shows all the records in the dataset.

procedure TFormDataBinding.UpdateLinks;
var
  it: TTMSFNCDataBinderItem;
begin
  TMSFNCDataBinder1.BeginUpdate;
  it := TMSFNCDataBinder1.Items.Add;
  it.&Object := ListBox1;
  it.BindType := dbbtList;
  it.DataSource := DataSource1;
  it.FieldName := 'Common_Name';
  it.PropertyName := 'Items';
  TMSFNCDataBinder1.EndUpdate;
  TMSFNCDataBinder1.Active := True;
end;
Again as with the single value binding, the below code uses a convenience method to add an item and set all the properties in one go.
procedure TFormDataBinding.UpdateLinks;
begin
  TMSFNCDataBinder1.ConnectList(ListBox1, DataSource1, 'Items', 'Common_Name');
  TMSFNCDataBinder1.Active := True;
end;
Result:

TMSFNCDataBinding3

Binding a list (TCollection)

Binding a list based on TCollection is similar to binding a list based on TStringList. When binding a TStringList based component it is sufficient to set the property that represents the list (Items in case of a TListBox). Some components have a collection to represent the items and when using the above, it will add items to the collection but not visualize the content, as typically there is a Caption or Text property inside the TCollectionItem that is used to display the values inside the list.

To work with a TCollection instead of a TStringList in the databinder, you need to use the SubPropertyNames & the SubFieldNames collection and this immediately allows you to bind more than one sub-property. The PropertyName remains “Items”, but for each collection item that is automatically added to the “Items” collection, the SubPropertyNames & SubFieldNames collection specifies which properties of that item are bound to which fields. For each item inside the SubPropertyNames there is an equivalent in the SubFieldNames and vice versa. Below is a sample based on the TTMSFNCListBox component.

procedure TFormDataBinding.UpdateLinks;
var
  it: TTMSFNCDataBinderItem;
begin
  TMSFNCDataBinder1.BeginUpdate;
  it := TMSFNCDataBinder1.Items.Add;
  it.&Object := TMSFNCListBox1;
  it.BindType := dbbtList;
  it.DataSource := DataSource1;
  it.PropertyName := 'Items';
  it.SubPropertyNames.Add.Value := 'Text';
  it.SubFieldNames.Add.Value := 'Common_Name';
  TMSFNCDataBinder1.EndUpdate;
  TMSFNCDataBinder1.Active := True;
end;
or
procedure TFormDataBinding.UpdateLinks;
begin
  TMSFNCDataBinder1.ConnectList(TMSFNCListBox1, DataSource1, 'Items', ['Text'], ['Common_Name']);
  TMSFNCDataBinder1.Active := True;
end;
Below is a sample that demonstrates how to bind more than one property.
procedure TFormDataBinding.FormCreate(Sender: TObject);
begin
  TMSFNCListBox1.DefaultItem.BitmapWidth := 32;
  TMSFNCListBox1.DefaultItem.BitmapHeight := 32;
  TMSFNCListBox1.ItemsAppearance.HeightMode := lihmVariable;
  TMSFNCDataBinder1.ConnectList(TMSFNCListBox1, DataSource1, 'Items', ['Text', 'Bitmap'], ['Common_Name', 'Graphic']);
  TMSFNCDataBinder1.Active := True;
end;
TMSFNCDataBinding4

Binding a column/list will retrieve all fields and all records and display them in a column/list structure. This means that the component you want to bind needs to have a separate column and a list collection.

procedure TFormDataBinding.FormCreate(Sender: TObject);
begin
  TMSFNCDataBinder1.ConnectColumnList(TMSFNCTreeView1, DataSource1, 'Nodes', 'Columns', 'Text', 'Values.Text');
  TMSFNCDataBinder1.Active := True;
end;

TMSFNCDataBinding5

Binding a grid

Binding a grid is only possible when the component you are targeting implements the ITMSFNCDataBinderGrid interface. You can either use the interface, or redefine the interface with the correct GUID.

ITMSFNCDataBinderBase = interface
['{778B63C9-34E3-4B65-A6B8-85E3EB1D17C3}']
  procedure DataBeginUpdate;
  procedure DataEndUpdate;
end;

ITMSFNCDataBinderGrid = interface(ITMSFNCDataBinderBase)
['{D23BDEAA-49B1-451A-9401-0D0D11A9957A}']
  procedure SetDataColumnCount(AValue: Integer);
  procedure SetDataRowCount(AValue: Integer);
  procedure ClearData;
  function GetDataRowCount: Integer;
  procedure SetDataValue(AColumn, ARow: Integer; AValue: string);
  procedure SetDataHeader(AColumn: Integer; AValue: string);
end;

Then after implementing the correct interfaces, you can use the following code to bind to a grid

procedure TFormDataBinding.FormCreate(Sender: TObject);
begin
  TMSFNCDataBinder1.ConnectGrid(TMSFNCGrid1, DataSource1);
  TMSFNCDataBinder1.Active := True;
end;

TMSFNCDataBinding6

Binding a TList/TObjectList

Next to TStringList & TCollection there is also support for TList & TObjectList properties based on Integer & Strings. The sample below defines a TPersistent object with 2 published properties, bound to a data source.

TListObject = class(TPersistent)
private
  FL: TList<Integer>;
  FS: TList<string>;
public
  constructor Create;
  destructor Destroy; override;
published
  property L: TList<Integer> read FL;
  property S: TList<string> read FS;
end;

procedure TFormDataBinding.Bind;
var
  o: TListObject;
begin
  o := TListObject.Create;
  try
    TMSFNCDataBinder1.Items.Clear;
    TMSFNCDataBinder1.ConnectList(o, DataSource1, 'L', [], ['Price']);
    TMSFNCDataBinder1.ConnectList(o, DataSource1, 'S', [], ['Brand']);

    o.Log;

    ClientDataSet1.First;
    ClientDataSet1.Edit;
    ClientDataSet1.Fields[1].AsString := 'Hello World!';
    ClientDataSet1.Post;

    o.Log;
  finally
    o.Free;
  end;
end;

Result

Debug Output: 
{"$type":"TListObject","L":[699000,1268000,2096000,2000000,3881000,1659000,929000,9740500,690
000,2010000,2035825,1175000,4779500,3950000,1685000,1259000,7410000,4537000,12445000,9065
00,1053000],"S":["Alfa Romeo","MERCEDES","TVR","Wiesmann","Honda","Lexus","Mazda","Rolls 
Royce","Audi","JAGUAR","MASERATI","LOTUS","FERRARI","BMW","PORSCHE","PEUGEOT","LAMBORGHINI"
,"DE TOMASO","MERCEDES","MG","Chrysler"]} Process DataBindingTest.exe (1612)

And the result after editing a value to “Hello World!”

Debug Output: 
{"$type":"TListObject","L":[699000,1268000,2096000,2000000,3881000,1659000,929000,9740500,690
000,2010000,2035825,1175000,4779500,3950000,1685000,1259000,7410000,4537000,12445000,9065
00,1053000],"S":["Hello World","MERCEDES","TVR","Wiesmann","Honda","Lexus","Mazda","Rolls 
Royce","Audi","JAGUAR","MASERATI","LOTUS","FERRARI","BMW","PORSCHE","PEUGEOT","LAMBORGHINI"
,"DE TOMASO","MERCEDES","MG","Chrysler"]} Process DataBindingTest.exe (14732)

Sub collections

In the binding column/list chapter, you can see the sub property that is being targeted for the TTMSFNCTreeView “Nodes” collection is “Values.Text”. There is ofcourse no property named this way, but when activating the database adapter, the “Nodes” collection is filled with items. Each item represents a node (TTMSFNCTreeViewNode), and each node has a property called “Values”. This represents the value for each column. When adding a TTMSFNCTreeViewNodeValue inside the “Values” collection, you can set a “Text” property that will then be displayed in the treeview. To make this work we have added support in the database adapter to go a level deeper when necessary. When the sub property under the main property is also a TCollection, it will add a value and then access the property afterwards and set the value of the field accordingly.

TMSFNCDataBinder1.ConnectList(TMSFNCTreeView1, DataSource1, 'Nodes', ['Values.Text', 'Values.Text', 'Values.CollapsedIcon'], ['Common_Name', 'Category', 'Graphic']);

TMSFNCDataBinding7

HTML Template

For the single value and list bindings, there is support to add a HTML template instead of binding to a specific field name. The HTML template supports multiple fields as long as they follow a specific kind of format. The HTML itself is based on the mini HTML reference https://www.tmssoftware.com/site/minihtml.asp

The format for adding fields is: <#FIELDNAME>.

So when applying this to the single value binding for example we can add an item using the

TMSFNCDataBinder1.ConnectSingleHTMLTemplate(TMSFNCHTMLText1, DataSource1, 'Text', '<b>Name: <#COMMON_NAME></b><br/><#NOTES>');
or
procedure TFormDataBinding.UpdateLinks;
var
  it: TTMSFNCDataBinderItem;
begin
  TMSFNCDataBinder1.BeginUpdate;
  it := TMSFNCDataBinder1.Items.Add;
  it.&Object := TMSFNCHTMLText1;
  it.BindType := dbbtSingleValue;
  it.DataSource := DataSource1;
  it.PropertyName := 'Text';
  it.HTMLTemplate := '<b>Name: <#COMMON_NAME></b><br/><#NOTES>';
  TMSFNCDataBinder1.EndUpdate;
  TMSFNCDataBinder1.Active := True;
end;
This will result in:

TMSFNCDataBinding8

Editor

The databinder component installs an editor that is available at designtime and at runtime. To start it at runtime, call

TMSFNCDataBinder1.ShowEditor;
At designtime, you can right-click the editor and select “Edit…” to start the editor.

TMSFNCDataBinding9

The editor allows you to edit, add and delete bindings. Click on Edit Mode to make changes to existing bindings. Click on the + sign at the top left to add a new binding and when in edit mode, select new components to start a new configuring a new binding. The comboboxes will list properties based on the bind component and the datasource. The datasource can be selected in the upper right corner next to the bind type.

Notifying the DataBinder of Changes

Overview

The TMS FNC DataBinder component provides a sophisticated notification system that allows bound controls and components to inform the databinder when their values have changed. This enables the databinder to automatically synchronize the updated values back to the underlying dataset, maintaining data consistency between the user interface and the data layer.

Notification Architecture

Notification Types

The databinder supports several types of notifications, defined by the TTMSFNCDataBinderNotificationType enumeration:

TTMSFNCDataBinderNotificationType = (
  dbntUpdate,           // General update notification
  dbntEdit,             // Edit mode notification
  dbntPost,             // Post changes notification
  dbntValueChanged,     // Value has changed notification
  dbntSetActiveRecord   // Active record selection notification
);

Notification Interfaces

ITMSFNCDataBinderNotification

Controls and components can implement this interface to participate in the notification system:

ITMSFNCDataBinderNotification = interface
  ['{3CEED28B-A125-4B26-B765-72785F2B0180}']
  function GetObject: TObject;
  procedure SetObject(const Value: TObject);
  procedure Notify(AType: TTMSFNCDataBinderNotificationType);
  property &Object: TObject read GetObject write SetObject;
end;
ITMSFNCDataBinderNotificationManager

The notification manager interface handles the distribution of notifications:

ITMSFNCDataBinderNotificationManager = interface
  ['{AFAFFAD9-AA33-4063-9BCB-6A17EC97D5C1}']
  procedure Notify(AControl: TObject; AType: TTMSFNCDataBinderNotificationType);
end;

Notification Methods

Direct Notification Methods

The TTMSFNCDataBinderItem class provides several convenience methods for triggering specific types of notifications:

NotifyValueChanged

procedure NotifyValueChanged;
Call this method when the bound control's value has changed and needs to be written to the dataset.

NotifyEdit

procedure NotifyEdit;
Triggers edit mode on the dataset, preparing it for value updates.

NotifyPost

procedure NotifyPost;
Posts pending changes to the dataset.

NotifyUpdate

procedure NotifyUpdate;
Forces a general update of the databinding relationship.

NotifySetActiveRecord

procedure NotifySetActiveRecord;
Updates the active record position in the dataset based on the control's selection.

Master Notification Method

All notification methods eventually call the central Notify method:

procedure Notify(AType: TTMSFNCDataBinderNotificationType);

This method handles the actual notification processing based on the type:

case AType of
  dbntSetActiveRecord:
  begin
    // Update dataset record position based on control selection
    if Supports(&Object, ITMSFNCDataBinderSelection, bs) then
    begin
      r := bs.DataGetItemIndex + 1;
      if r >= 1 then
        FDataLink.DataSource.DataSet.RecNo := r;
    end;
  end;
  dbntUpdate: UpdateDataLink(dlmUpdateData);
  dbntEdit: DataSetEdit;
  dbntValueChanged: WriteValueToField;
  dbntPost: DataSetPost;
end;

Automatic Monitoring

Observer Pattern Implementation

The databinder can automatically monitor bound controls using the observer pattern, eliminating the need for manual notifications in many cases.

Control Value Monitor

For controls that support the IControlValueObserver interface:

TTMSFNCDataBinderControlValueMonitor = class
  procedure StartMonitoring;
  procedure StopMonitoring;
  property Active: Boolean read FActive;
end;

For controls that support the IEditLinkObserver interface:

TTMSFNCDataBinderEditLinkMonitor = class
  procedure StartMonitoring;
  procedure StopMonitoring;
  property Active: Boolean read FActive;
end;

Automatic Observer Setup

When a databinder item is created and configured, it can automatically set up observers for supported controls:

function GetControlValueMonitor: TTMSFNCDataBinderControlValueMonitor;
begin
  if not Assigned(FControlValueMonitor) and 
     not FDataBinder.HasEditLinkMonitor(&Object) and 
     not FDataBinder.HasControlValueMonitor(&Object) then
  begin
    FControlValueMonitor := TTMSFNCDataBinderControlValueMonitor.Create(Self);
    FControlValueMonitor.StartMonitoring;
  end;
  Result := FControlValueMonitor;
end;

Manual Notification Examples

Basic Value Change Notification

// When a control's value changes
procedure TMyControl.ValueChanged;
var
  DataBinderItem: TTMSFNCDataBinderItem;
begin
  // Find the databinder item for this control
  DataBinderItem := MyDataBinder.ItemByObject[Self, 'Text'];
  if Assigned(DataBinderItem) then
    DataBinderItem.NotifyValueChanged;
end;

Edit and Post Sequence

// Complete edit sequence
procedure TMyForm.UpdateRecord;
var
  DataBinderItem: TTMSFNCDataBinderItem;
begin
  DataBinderItem := MyDataBinder.ItemByObject[MyEdit, 'Text'];
  if Assigned(DataBinderItem) then
  begin
    DataBinderItem.NotifyEdit;        // Put dataset in edit mode
    DataBinderItem.NotifyValueChanged; // Write the value
    DataBinderItem.NotifyPost;        // Post the changes
  end;
end;

Selection Change Notification

// When a list control's selection changes
procedure TMyListControl.SelectionChanged;
var
  DataBinderItem: TTMSFNCDataBinderItem;
begin
  DataBinderItem := MyDataBinder.ItemByObject[Self, 'Items'];
  if Assigned(DataBinderItem) then
    DataBinderItem.NotifySetActiveRecord;
end;

Global Notification

DataBinder-Level Notifications

The main databinder component provides methods to notify all bound items:

// Notify all bound controls
MyDataBinder.NotifyValueChanged;  // All items write their values
MyDataBinder.NotifyEdit;          // All items enter edit mode
MyDataBinder.NotifyPost;          // All items post their changes
MyDataBinder.NotifyUpdate;        // All items refresh from dataset
MyDataBinder.NotifySetActiveRecord; // All items update active record

WriteValueToField Implementation

The core method that actually writes the control value to the dataset:

procedure WriteValueToField;
begin
  if CheckDataSet and (DataSetIsEditing or DataSetIsInserting) then
  begin
    FDirty := True;
    UpdateDataLink(dlmUpdateData);
  end;
end;

This method: 1. Checks if the dataset is active and in edit mode 2. Marks the item as dirty 3. Triggers a data link update to write the value

Best Practices

1. Use Automatic Monitoring When Possible

Let the databinder automatically monitor standard controls rather than implementing manual notifications.

2. Implement Notification Interfaces for Custom Controls

For custom controls, implement ITMSFNCDataBinderNotification to participate in the notification system.

3. Batch Notifications

When updating multiple controls, use the global notification methods to ensure consistency.

4. Handle Edit Mode Properly

Always ensure the dataset is in edit mode before attempting to write values:

if DataBinderItem.DataSetCanModify then
begin
  DataBinderItem.NotifyEdit;
  DataBinderItem.NotifyValueChanged;
end;

5. Error Handling

Wrap notification calls in try-except blocks for robust error handling:

try
  DataBinderItem.NotifyValueChanged;
except
  on E: Exception do
    ShowMessage('Error updating data: ' + E.Message);
end;

Note

All notification methods eventually call the central Notify method which handles the actual notification processing based on the type.

Tip

Use automatic monitoring when possible - let the databinder automatically monitor standard controls rather than implementing manual notifications.

Important

Always ensure the dataset is in edit mode before attempting to write values to prevent data integrity issues.

Conclusion

The TMS FNC DataBinder notification system provides flexible and powerful mechanisms for keeping bound controls synchronized with dataset values. By understanding and properly utilizing these notification methods, developers can create responsive data-aware applications that automatically maintain data consistency between the user interface and underlying data sources.