Skip to content

XE3 Visual LiveBindings: User defined C++ objects

In the previous post, TListBindSourceAdapter<TContact> is used to enable a list of Delphi TContact objects with LiveBindings, using the CreateAdapter event handler:

implementation

{$R *.dfm}

uses ContactsUnit, System.Generics.Collections;

procedure TForm1.AdapterBindSource1CreateAdapter(Sender: TObject;
  var ABindSourceAdapter: TBindSourceAdapter);
begin
  ABindSourceAdapter := TListBindSourceAdapter<TContact>.Create(Self,
    LoadContacts);
end;

C++ custom object can also be enabled for LiveBindings.   Here is a C++ version of TContact for example:

#pragma explicit_rtti methods (__published, public) properties (__published, public) fields(__published, public)
class  TContact: public TObject
{
private:
  System::String firstname;
  System::String lastname;
public:
  __property System::String FirstName  = {read=firstname, write=firstname};
  __property System::String LastName  = {read=lastname, write=lastname};

  TContact()
  {
    firstname = "<first>";
    lastname = "<last>";
  }

  TContact(const System::String first, const System::String last)
  {
    firstname = first;
    lastname = last;
  }

};

The implementation of the CreateAdapter event is a little different than Delphi.  C++ can’t create a new generic type like TListBindSourceAdapter<TContact>.  The non-generic version of TListBindSourceAdapter is used instead.

void __fastcall TForm1::AdapterBindSource1CreateAdapter(TObject *Sender, TBindSourceAdapter *&ABindSourceAdapter)

{
  ABindSourceAdapter = new TListBindSourceAdapter(this,
    LoadContacts(), __classid(TContact), true);

}

Note that the class type is passed as a parameter.

A C++ sample project is available on source forge:
https://radstudiodemos.svn.sourceforge.net/svnroot/radstudiodemos/branches/RadStudio_XE3/LiveBindings/observers

The project is called CppListAdapterEventProject.dpr

XE3 Visual LiveBindings: User defined objects

The adapter components TListBindSourceAdapter and TObjectBindSourceAdapter enable LiveBindings with user defined TObjects.

Here is a user defined TObject  for example.

type
  TContact = class
  private
    FFirstName: string;
    FLastName: string;
  public
    constructor Create; overload;
    constructor Create(const AFirstName, ALastName: string); overload;
    property FirstName: string read FFirstName write FFirstName;
    property LastName: string read FLastName write FLastName;
  end;

  function LoadContacts: TList<TContact>;
implementation

function LoadContacts: TList<TContact>;
begin
  Result := TObjectList<TContact>.Create;
  // TODO: Load data from file
  Result.Add(TContact.Create('Adam', 'Barns'));
  Result.Add(TContact.Create('Chris', 'Decker'));
  Result.Add(TContact.Create('Emma', 'Fisk'));
end;

One way to use TListBindSourceAdapter is to instantiate the adapter at run time.  Drop a TAdapterBindSourceAdapter on the form and implement the CreateAdapter event as follows:

mplementation

{$R *.dfm}

uses ContactsUnit, System.Generics.Collections;

procedure TForm1.AdapterBindSource1CreateAdapter(Sender: TObject;
  var ABindSourceAdapter: TBindSourceAdapter);
begin
  ABindSourceAdapter := TListBindSourceAdapter<TContact>.Create(Self,
    LoadContacts);
end;i

In the LiveBindings designer, we won’t see the fields for TContact because the TContact type is not known at design time.

Drop a TStringGrid on the form and connection the ‘*’ of AdapterBindSource1 to StringGrid1.

A navigator can be added by right clicking AdapterBindSource1 and choosing "Add Navigator".

The grid is populated when the application is run:

A TDataGeneratorAdapter can be used to provide some fields to use at design time.  Add fields with the same name as the properties in TContact.  Set AdapterBindSource1.Adapter to DataGeneratorAdapter1.

The form shows sample data and the LiveBindings designer shows field names.

To show this data at design time, the TDataGeneratorAdapter fields have FieldType: ftString and Generator: LoremIpsum.

The LiveBindings designer can be used to connect fields to controls.

Note that an easy way to create a TLabeledEdit is to right click on FirstName then choose "Link to new control…".

At runtime, the TListBindSourceAdapter will be used instead of DataGeneratorAdapter1 because of the CreateAdapter event.

Another way to use TListBindSourceAdapter is to register a custom component.  Instead of instantiating a TListBindSourceAdapter in the CreateAdapter event, the custom component is dropped from the tool palette.  A TDataGeneratorAdapter is not needed.

The minimal code to for a custom adapter looks like this:

unit ContactsAdapter;

interface

  uses ContactsUnit, Data.Bind.ObjectScope, System.Classes, System.Generics.Collections;

type

  TContactsAdapter = class(TListBindSourceAdapter<TContact>)
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

{ TContactsAdapter }

constructor TContactsAdapter.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  SetList(LoadContacts);
end;

To register the custom adapter, you will need to create and register a package which contains the custom adapter component and TContact class.  You will also need and a Register method to register the custom component.

unit ContactsAdapterReg;

interface

procedure Register;

implementation

uses System.Classes, ContactsAdapter, ContactsAdapter2;

procedure Register;
begin
  RegisterComponents('Test', [TContactsAdapter]);
end;

end.

A complete implementation of a custom adapter publishes properties and methods:

  TContactsAdapter2 = class(TListBindSourceAdapter<TContact>)
  public
    constructor Create(AOwner: TComponent); override;
  published
    property OnBeforeSetList;
    property OnAfterSetList;
    property OnListAppend;
    property OnListDelete;
    property OnListInsert;
    property OnCreateItemInstance;
    property OnInitItemInstance;
    property Options;
    property AutoEdit;
    property BeforeOpen;
    property AfterOpen;
    property BeforeClose;
    property AfterClose;
    property BeforeInsert;
    property AfterInsert;
    property BeforeEdit;
    property AfterEdit;
    property BeforePost;
    property AfterPost;
    property BeforeCancel;
    property AfterCancel;
    property BeforeDelete;
    property AfterDelete;
    property BeforeScroll;
    property AfterScroll;
    property BeforeRefresh;
    property AfterRefresh;
    property BeforeApplyUpdates;
    property AfterApplyUpdates;
    property BeforeCancelUpdates;
    property AfterCancelUpdates;
    property OnDeleteError;
    property OnInsertError;
    property OnEditError;
    property OnPostError;
    property OnApplyUpdatesError;
    property OnCancelUpdatesError;
    property OnApplyUpdates;
    property OnCancelUpdates;
    property OnHasUpdates;
  end;

TObjectBindSourceAdapter is used instead of TListBindSourceAdapter to bind to a single object, rather than a list of objects.

TObjectBindSourceAdapter is instantiated like this:

procedure TForm1.AdapterBindSource1CreateAdapter(Sender: TObject;
  var ABindSourceAdapter: TBindSourceAdapter);
begin
  ABindSourceAdapter := TObjectBindSourceAdapter<TContact>.Create(Self,
    TContact.Create('Adam', 'Barns'));
end;

At run time, there is only a single row of data.

Source forge samples

https://radstudiodemos.svn.sourceforge.net/svnroot/radstudiodemos/branches/RadStudio_XE3/LiveBindings/observers

  • ContactsAdapterPackage.dpk
    • Implements a custom TListBindSourceAdapter component
  • ListAdapterEventProject.dpr
    • Demonstrates  TAdapterBindSource.CreateAdapter event
  • ListAdapterComponentProject.dpr
    • Demonstrates custom TListBindSourceAdapter component
  • Project: CppListAdapterEventProject.dpr
    • Cpp project demonstrates  TAdapterBindSource.CreateAdapter event

XE3 Visual LiveBindings: Samples

Here is a summary of the samples which accompany my XE3 VLB posts.  The samples are grouped by Sourceforge locations.

  • Observers (post)
    • VclSampleObservableControls.dpk - Implements an observable TTrackBar
    • LinkTrackbarToField.dpr - TObservableTrackBar project
  • Actions (post)
    • Navigate using actions and TSpeedButton
      • Vcl\LiveBindingsActionsProtoProject.dpr
      • Fmx\FmxLiveBindingsActionsProtoProject.dpr
      • Vcl\LiveBindingsActionCDSProject.dpr
      • Fmx\FmxLiveBindingsActionsCDSProject.dpr
  • ListControls (post1, post2)
    • ListBoxVCL.dpr - Link a TListBox to a field
    • SimpleListView.dpr - Link a TListView to a field
    • LiveViewColumns.dpr - Link a TListView to a field and fill multiple columns
  • Lookups (post1, post2)
    • LookupListBoxVCL.dpr - Configure TListBox as a lookup list
    • LookupEditVCL.dpr - Configure a TLabeledEdit as a lookup control
    • LookupLabelVCL.dpr - Configure TLabel.Caption as a lookup property

XE3 Visual LiveBindings: TListView

A previous post uses TListBox to demonstrate how to link fields to a list control.  The same steps can be used to link fields to TListView.

Here is a sample form with a TListView:

To improve the TListView appearance, the ListView1.ViewStyle property has been set to vsList.

The LiveBindings designer looks like this:

A project containing this form can be downloaded here.  See SimpleListView.dpr.

The LiveBindings designer also supports TListView columns.  Follow these steps to add columns to ListView1:

  1. Select ListView1
  2. View the object inspector
  3. Use the object inspector to change properties of ListView1
    • Change ListView1.ViewStyle to vsReport
    • Click on the "…" to the right of the ListView1.Columns property to show the columns editor
    • Use the columns editor to add three columns

The LiveBindings designer shows additional member for ListView1:

"Item.Caption" is the only member linked to a field, so the form looks like this:

To improve the TListView appearance,  use the object inspector to set column captions and widths.

To fill the additional columns, add two fields to TPrototypeBindSourceFill and connected the new fields to the "SubItems" members.

After these changes, the form looks like this:

The design looks like this:

A project containing this form can be downloaded here.  See ListViewColumns.dpr.

XE3 Visual LiveBindings: Lookup controls

A lookup control displays a value that is different from the actual field value.

In this post, I modify the sample application built in the previous post about lookup lists.

The lookup list example ended with this design:

The lookup list ended with this form:

To demonstrate a lookup control, this form is changed so that the TLabeledEdit displays a color name instead of a color number. For example, "DarkGreen" is displayed instead of "25600".

To accomplish this, a "LookupDataSource" is configured in the object inspector.

First, show the LiveBindings designer by selecting the "View/LiveBindings designer" command. Click the line connecting LabeledEditColor1.Text and PrototypeBindSource1.Text to select the binding component.

The properties of LinkControlToField1 are shown in the object inspector:

Using the object inspector, change properties of LinkControlToField1 to use a "LookupDataSource":

  1. Set "LookupDataSource" to PrototypeBindSourceFill
  2. Set "LookupKeyFieldName" to "AColor"
  3. Set "LookupValueFieldName" to "AColorName"

After configuring the "LookupDataSource", the LiveBindings designer shows the relationship between the data sources and the TLabeledEdit as follows.

The form designer looks like this, with the TLabeledEdit displaying a color name rather a color number:

The source for this sample is available here. See LookupEditVCL.dpr.

A "LookupDataSource" can also be used with links between component properties and fields. For example, in this form, the property TLabel.Caption displays a color name using a lookup.

The "LookupDataSource" for a TLinkPropertyToField component is configured using the object inspector, just as the TLinkControlToField component is configured.

The source for this sample is available here. See LookupLabelVCL.dpr.

XE3 Visual LiveBindings: Link a field to a lookup list

A lookup list is used when the actual data values are not meaningful to users. A lookup list populates a list control with display values, instead of actual data values.
We can configure two TPrototypeBindSource components to represent a situation that calls for a lookup list. These steps differ from the previous post by using a Colors field in addition to a ColorsName field. The Colors field holds a numeric color value.
First, create the "DataSource".
  1. New VCL application
  2. Drop a TPrototypeBindSource on the form
  3. In the object inspector, set PrototypeBindSource1.RecordCount to 10
  4. Add a Colors field to the TPrototypeBindSource

Next, create the "FillDataSource"

  1. Drop a second TPrototypeBindSource on the form
  2. In the object inspector, rename to "PrototypeBindSourceFill"
  3. Add Colors and ColorsNames fields to the PrototypeBindSourceFill
  4. Uncheck "Shuffle" and "Repeat" to generate an ordered list of unique values

Drop a TListBox on the form

Choose the "View/LiveBindings designer" command to show the LiveBindings designer:

For clarity, rename PrototypeBindSourceFill fields to AColor and AColorName. Select each field in the LiveBindings designer and use the object inspector to type in a new name:

In order to create a lookup list, the "FillDataSource" must provide a "ValueField" and a "DisplayField". These two fields are used to build a dictionary which can be used to lookup a display value for each actual value.
In this example, AColor is the "ValueField" and AColorName is the "DisplayField". A numeric color value in AColor can be used to lookup a color name in AColorName.

Make three connections in the LiveBindings designer to create a lookup list:

  1. Connect "PrototypeBindSource1.Color1" to "ListBox1.SelectedText"
  2. Connect "PrototypeBindSourceFill.AColorName" to "ListBox1.ItemText".
  3. Connect "PrototypeBindSourceFill.AColor" to "ListBox1.LookupData".

ListBox1 is populated in the form designer:

"Darkgreen" is selected because the value of the "Colors1" field in the first record of PrototypeBindSource1 is paired with "Darkgreen" in PrototypeBindSourceFill.

Right click on PrototypeBindSource1 and choose "Add Navigator". Now the app is ready to run.

I’ve also added a TLabeledEdit and a TShape.

The TLabeledEdit shows the value of PrototypeBindSource1.Color1 as a number.

The TShape shows the value of PrototypeBindSource1.Color1 field as a color.

Here is the complete design:

The source for this sample is available here.  See LookupListBoxVCL.dpr.


XE3 Visual LiveBindings: Link a list control to a field

List controls can be linked to data source fields. TListBox, TComboBox and VCL TListView are examples of list controls. When a list control is linked to a data source field, the selected item in the list control corresponds to the value of the data source field. To change the value of the field, the user selects a different item in the list control.

Typically, two data sources are used with a list control. One data source provides the value to select in the list control. The other data source provides the values to fill the list control. LiveBindings uses the terms "DataSource" and "FillDataSource".

The TPrototypeBindSource is a simple way to create data sources. We can configure one TPrototypeBindSource as the "DataSource" and another as the "FillDataSource".

First, create the "DataSource".

  1. New VCL application
  2. Drop a TPrototypeBindSource on the form
  3. In the object inspector, set PrototypeBindSource1.RecordCount to 10
  4. Add a ColorsName field to the TPrototypeBindSource (See this post for instructions on adding fields)

Next, create the "FillDataSource"

  1. Drop a second TPrototypeBindSource on the form
  2. In the object inspector, rename to "PrototypeBindSourceFill"
  3. Add a ColorsName field to the PrototypeBindSourceFill
  4. Name the field "AColorsName"
  5. Uncheck "Shuffle" and "Repeat" to generate an ordered list of unique values

Drop a TListBox on the form.

Choose the "View/LiveBindings designer" command to show the LiveBindings designer:

In the LiveBindings designer, ListBox1 is shown with three members: "SelectedValue", "Item.Text", and "Item.LookupData".

Connect "PrototypeBindSource1.ColorsName1" to "ListBox1.SelectedValue". Connect "PrototypeBindSourceFill.AColorsName" to "ListBox1.Item.Text".

ListBox1 is populated in the form designer.

"Darkgreen" is selected because the value of the "ColorsName1" field in the first record of PrototypeBindSource1 is "Darkgreen".

Right click on PrototypeBindSource1 and choose "Add Navigator". Now the app is ready to run.

The selection in the list box will change as the user changes the active record using the navigator.
I’ve added a TLabeledEdit to show how selecting a value in the listbox changes the value of the field.

Here is the complete design:

My next post will describe how to use the "Item.LookupData" member to create a lookup list.

XE3 Visual LiveBindings: Hiding and showing components

This post describes how to specify which components show on the LiveBindings designer.

Hide/Show Component Classes

To reduce clutter, the LiveBindings designer hides many classes of components. The default settings show most visual control classes and hide most non-visual components classes except for data sources.

The settings are in Tools/Options/LiveBindings:

If there is a component class that you always want to see on the LiveBindings designer, add it to the "Included Items:" field. For example, if you use FireMonkey 3D controls then you’ll probably want to add "TControl3D".

This setting will cause components derived from TControl3D, like TText3D, to show in the LiveBindings designer.

Hide/Show Component instances

To hide a specific component, right click on the component in the LiveBindings designer and choose "Hide Elements(s)".

To show a hidden component, use the object inspector to change the LiveBindings Designer properties for the component.

LiveBindings Designer properties for a components supersede the settings for the component’s class in Tools/Options. LiveBindings Designer properties for components are saved in unitname.vlb file with other LiveBindings designer state information.

Link to TForm

The LiveBindings designer can be configured to show the TCustomForm class, but the form instance will not show in the designer until the form is linked.

To configure the LiveBindings designer to show TCustomForm, prepend the "Included Items:" setting with "TCustomForm;".

A link can be created to a TLabel and then modified using the object inspector.

  1. New FireMonkey application
  2. Drop a TEdit and TLabel on the form
  3. In the LiveBindings designer, link TEdit.Text to TLabel.Text
  4. Select the link component by clicking on the line between the components
  5. In the object inspector, change the "ComponentProperty" property to blank
  6. Change the "Component" property to "Form1"
  7. Change the "ComponentProperty" property to "Caption"
  8. Delete the TLabel component

XE3 Visual LiveBindings: Actions

LiveBindings actions are new in XE3.  If you are not familiar with actions, follow this link.  LiveBindings actions correspond to the buttons on the TBindNavigator such as First, Next, Edit, Post, and Delete.

Steps for creating a speed button which uses a LiveBindings action:

  1. Drop a  TPrototypeBindSource on the form and add fields.  See this post.
  2. Drop a  TActionList
  3. Drop a TSpeedButton
  4. In the object inspector, click the down arrow next to the SpeedButton1.Action property.
    • Choose "New Standard Action/LiveBindings/TBindNavigateNext"
  5. Set the BindNavigateNext1.DataSource property to PrototypeBindSource1.

Here is a FireMonkey form using LiveBindings actions:

VCL:

Tips

  • To use a TClientDataSet, instead of a TPrototypeBindSource, you will also need a TBindSourceDB.  Set the TBindSourceDB.DataSet property to the TClientDataSet.  Set the action’s DataSource property to the TBindSourceDB.
  • If a speed button stays disabled,  check that the action’s DataSource property is set.

XE3 Visual LiveBindings: Link controls to component properties

Simple controls can be linked to component properties.   TEdit is an example of a simple control.  TLabel.Text is an example of a component property.   Controls can be linked to most public properties of any component on a form.

Link controls to component properties in the LiveBindings designer

After a TEdit and TLabel are dropped on a FireMonkey form, the LiveBindings designer represents the TEdit with the member "Text" and the TLabel with the member "Text":

The LiveBindings designer can be used to connect these members:

In the form you will notice that the value of the label has been copied to the edit.  When the link is initialized, the component property value is copied to the control to give both members the same value to start off.  After initialization, the control value will be copied to the component property whenever the user modifies the edit and presses enter, or leaves the edit.

In the LiveBindings designer, the line between the components represents a link component.  Click the line to select the component in the object inspector.

The AutoActivate property indicates whether this link should be initialized when the application is run.  This property also activates the link at design time.

The InitializeControlValue property can be unchecked to prevent the component property value from being copied to the control when the link is initialized.

Multiple links

The LiveBindings designer can be used to connect a single control to multiple component properties.

Links not permitted

LiveBindings does not permit links between component properties, such as Label1.Text and Button1.Text.

The LiveBindings designer highlights members when a link is not permitted.

LiveBindings does not permit a direct link between two user input control,  such as Edit1.Text to Edit2.Text. To achieve the same effect, link both controls to a TPrototypeBindSource field.

The links shown in this image will keep all "Text" members synchronized when the user modifies one of the edits then presses Enter, or exits the edit.

The TPrototypeBindSource.RecordCount property can be set to "1" when using a TPrototypeBindSource to provide an intermediate field.

Linking to other properties

To show additional component properties in the LiveBindings designer, click "…" to open the "Bindable Members" dialog.

This dialog populates a list with properties found by scanning the type information of the component.    Some properties are skipped because the type, such as sets, is not supported in LiveBindings expressions.  Put a check mark next to a property to show it in the LiveBindings designer.

Connecting from a control to "…" is another way to get at additional properties.  For example, drag from "IsChecked" to "…" and select the "WordWrap" property.

LiveBindings-enable component properties

LiveBindings uses RTTI to read and write component properties.  So no changes are needed to make a component’s properties linkable.

To make linking to commonly used properties easier, there is a registration method to indicate which member to show, by default, in the LiveBindings designer.   For example, RegisterValuePropertyName() is used to register "Text" as the default member of a FireMonkey TLabel and "Caption" as the default member of VCL TLabel.  See Fmx.Bind.Editors.pas and Vcl.Bind.Editors.pas.  RegisterValuePropertyName() is in Data.Bind.Components.

LiveBindings-enable controls

Controls must implement the ControlValue observer in order to support linking to component properties. My previous post has information about observers, including code for a sample control called TObservableTrackbar.    TObservableTrackBar implements the ControlValue observer, so supports links to component properties.  Here a TObservableTrackBar is linked to the "Width" property of a TShape.

Bad Behavior has blocked 26 access attempts in the last 7 days.

Close