Implementing a BindExpression in Code

I’m sorry.  It’s only post number two and I am already breaking my promise of what I intended to cover!  One of the comments after my initial post asked the question about implementing a BindExpression in code so I will deal with this topic today and I will make it up to you all who were waiting to hear about multiple control objects participate in a BindExpression, unidirectional and bidirectional bindings.

To illustrate the example of code based LiveBindings I will use the same FMX controls and BindExpression as in my first post so that we can compare apples to apples.

After creating my new FireMonkey HD Application, I Save All, save my form as FCodeBindExpression.pas & my project as CodeBindExpression.  I will finish off the implementation of the form towards the end.

The next step is to add a new unit to the project (File | New Unit).  I want this unit to hold a class called TDamoBindExpressionManager.  TDamoBindExpressionManager will have a single procedure called CreateSimpleBindExpression and this procedure will be responsible for creating a BindExpression.  Under the Interface declaration of the new unit, add the following class declaration:

type TDamoBindExpressionManager = class
  constructor Create();
  procedure CreateSimpleBindExpression(sourceObject, controlObject : TDamoBindData);
end;

You’ll see that in my procedure declaration, I have used a couple of parameters of type TDamoBindData - this is a record that contains the name of the object that I want to participate in the binding, the identifier or tag used to identify that object in the BindExpression and the property/expression to share the data.  I have created this record to minimise  the number of parameters that are required to create the binding.  From a design point of view, if I ever need to modify the parameters, I only need to update the record and not the procedure signature nor every place where I have called it.  Declare the TDamoBindData above your class declaration:

type TDamoBindData = record
  BindObject : TFMXObject;
  BindObjectTag : String;
  BindObjectExpression : String;
end;

Now we need to implement the procedure CreateSimpleBindExpression.  Right click on CreateSimpleBindExpression in the interface section of your unit and select "Complete Class at Cursor".  This will add the procedure declaration to the implementation section of your unit.  The first thing to do is to declare a variable for the BindExpression - I have named the variable bindExpression and this is of type TBindingExpression.

In order to instantiate bindExpression I will call the CreateManagedBinding function found in the TBindings class.  CreateManagedBinding contains a serious amount of parameters and I will do my best to describe each of them below:

InputScopes: The object that is considered your source object - the object that you want to get data from.

BindExprStr: This is the expression that determines how you will get data from your source object.

OutputScopes: The object that is to be used as the control object

OutputExpr: This is the expression that determines how you will use the data from your source object

OutputConverter: If the data returned from our source object needs to be ‘cast’ to another type then I can define an output converter to use (I will discuss output converters in one of my next posts).  For this example, the BindExpression requires no casting so I will pass in a nil parameter

Here is the implementation for the procedure CreateSimpleBindExpression:

procedure TDamoBindExpressionManager.CreateSimpleBindExpression(sourceObject, controlObject : TDamoBindData);
var
  bindExpression : TBindingExpression;
begin
  bindExpression := TBindings.CreateManagedBinding(
    { inputs }
    [TBindings.CreateAssociationScope([
      Associate(sourceObject.BindObject, sourceObject.BindObjectTag)
      ])],
    sourceObject.BindObjectExpression,
    { outputs }
    [TBindings.CreateAssociationScope([
      Associate(controlObject.BindObject, controlObject.BindObjectTag)
      ])],
    controlObject.BindObjectExpression,
    nil);
end;

There is nothing special in the default constructor so this is implemented as follows:

constructor TDamoBindExpressionManager.Create;
begin
  Inherited;
end;

There are a number of units that I am relying on to implement CreateSimpleBindExpression so I will need to add the following units to the implementations uses clause:

Data.Bind.Components, System.Bindings.Expression, System.Bindings.Helper;

Save the unit (I have named mine UDamoLiveBinding.pas) and we can continue onto the construction of FCodeBindExpression.  If the form isn’t already open in Delphi, double click on FCodeBindExpression in the Project Manager.  Add a TTrackBar and a TEdit just like we did in the example in my first post.  In order to register our BindExpression, I am going to instantiate TDamoBindExpressionManager and call the procedure CreateSimpleBindExpression.  Generate an OnCreate event by selecting the Form, choosing the Events tab in the Object Inspector and double clicking on OnCreate.  Before I implement the OnCreate event, I need to add a couple of units to the implementations uses clause:

UDamoLiveBinding, System.Bindings.Helper;

Implement the OnCreate event as follows:

procedure TForm1.FormCreate(Sender: TObject);
var
  damoBindExpressionManager : TDamoBindExpressionManager;
  sourceObject, controlObject : TDamoBindData;
begin
  sourceObject.BindObject := tbZoomy;
  SourceObject.BindObjectTag := 'tbZoomy';
  SourceObject.BindObjectExpression := 'tbZoomy.Value';
  ControlObject.BindObject := edtZoomyValue;
  ControlObject.BindObjectTag := 'edtZoomyValue';
  ControlObject.BindObjectExpression := 'edtZoomyValue.Text';
  damoBindExpressionManager := TDamoBindExpressionManager.Create();
  damoBindExpressionManager.CreateSimpleBindExpression(SourceObject, ControlObject);
  damoBindExpressionManager.Free;
end;

What I have added is the following:

Declare variables for our BindExpression (bindExpression), the TTrackBar (sourceObject) and the TEdit (controlObject)

Add the specific record items for the TTrackBar

Add the specific record items for the TEdit

Instantiate bindExpression

Register the bindExpression by calling CreateSimpleBindExpression

Free bindExpression

Similar to the example I used in my first post, I will generate an OnChange event for our source object (TTrackBar) and call the Notify procedure so that the system knows that a change has occurred and to call any BindExpressions that are registered with the source object as being the TTrackBar:

procedure TForm1.tbZoomyChange(Sender: TObject);
begin
  TBindings.Notify(Sender, 'Value');
end;

From a good design point of view,I should have created a ‘proxy’ procedure for this Notify call in TDamoBindExpressionManager that handled this notification.  By using a ‘proxy’ procedure, my form doesn’t need to know about TBindings, thus encapsulating the LiveBindings behaviour in TDamoBindExpressionManager. (I will leave that as an exercise for you!)

Running this example yields the same response that we saw in the last post.  From a personal viewpoint, I much prefer to make use of the TBindingList component to store my BindExpressions.  I think that the Editors provided by Delphi and C++ Builder make implementing each BindExpression a little easier to implement and to read!  I think code based LiveBindings are much more suited to Console Applications where there are no designers available but each to their own!  I hope that this was useful and allows you to compare apples to apples when considering implementing your BindExpressions through LiveBinding Components or directly in code.

Out. Damo.

Leave a Reply


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

Close