Once upon a time there was an App called iTimeSheet…

December 11th, 2012 by damienbootsma

Man, it’s been a while between posts!  So much has been happening since the release of XE3 to really keep the momentum of FireMonkey going.

Back in the day at Borland, we used to run workshops like ‘Jump start with Java’ and ‘Cruise through CORBA’ & the purpose of these was to walk you through the entire process of building a Java application or implementing a distributed architecture starting from a blank project to a ‘completed’ project.  With this approach, you could really see how the new features work in context of an application rather than just a Hello World application that showed a single feature.  I’m not saying that Hello World applications are wrong - they definitely have their place but in terms of a workshop I think it would be much more productive to build an application that linked a group of new features together so you can get an idea of how to use these new capabilities.  This is what I wanted to present for our Australian Developers.

After figuring out all of the visionary stuff, I needed to actually implement it!  I needed to find an application that would appeal to all & contain enough meat so it could be built in a couple of hours… enter iTimeSheet.

iTimeSheet currently is a FireMonkey desktop based application that stores all of it’s activity data in an Interbase XE3 desktop edition database.  This means that we can run it on Windows & OSX, including the database without any code changes.

The workshop is then focused on building this application in a couple of hours.  During this time we introduce the following FireMonkey features:

  • Layouts
  • Various Visual Components
  • Bind Expressions & Visual LiveBindings
  • Compiler directives
  • Actions & ActionList
  • Gestures & GestureManager
  • Animations
  • Styling
  • MetropolisUI

The construction of iTImeSheet also includes:

  • Understanding the current database schema
  • Use of DataModules
  • dbExpress
  • Refactoring the UI so that all business logic is managed in proxy classes.

I say currently in terms of iTimeSheet being a desktop based application because over time I would like to redesign it into a DataSnap application with perhaps an additional mobile and web based UI.  This redesign and implementation of new presentation layers could also be captured in another workshop down the track… if people are interested in seeing this :)

I took the workshop on the road to Brisbane, Melbourne & Sydney a few weeks back and we got some really good feedback to the format & the content - thanks heaps to all who came along and I hope to see you again soon!

Out. Damo.

HTML5 Builder & a mobile configuration trap for young players

September 11th, 2012 by damienbootsma

Recently I’ve been spending every spare moment testing out HTML5 Builder.  This is a new product in the Embarcadero stable - It builds on the features of Rad PHP but is a brand new IDE that supports many of the new, mainstream Web Development Technologies such as HTML5, CSS3, JavaScript & PHP.

One of the really cool features that was in RadPHP but has been enhanced is the Mobile Web Development capabilities.  Out of the box, we provide you with JQuery Mobile and Phonegap components, mobile theme support via ThemeRoller directly integrated into the IDE and wizards to build and deploy to devices that support iOS, Android, Windows Mobile or Blackberry.  As part of your installation of HTML5 Builder we ship the Android SDKs and Android Virtual Device (AVD) Manager that you can also choose to install during the HTML5 Builder installation process.

I was testing out the new development and deployment wizards for an Android mobile web application when I got to a point where the only option that I had for deployment was via Phonegap:Build - now Phonegap:Build is a new feature that wraps up all of your development artifacts and deploys them to the public Phonegap build service and returns you a shiny new app.  Now that’s all well and good but what if you are in a government office with restricted internet access? Or a financial institution where software has to be built internally due to security restrictions?  HTML5 Builder also has the necessary Phonegap and Android SDKs so that you can do this locally but I wasn’t getting this second option and I was also getting a warning about having an incorrect JAVA_HOME environment variable…

It turns out that Java and Windows 64 bit don’t play super nice together.  Java doesn’t like to be installed in Program Files (x86).  The first thing that I tried was to simply put quote marks (") around the JAVA_HOME (You can access youe environment variables via Control Panel | System | Advanced Settings | Environment Variables) value, this normally handles any spaces in directory names but it doesn’t seem to cope with the brackets so the next and final step was to uninstall  and then install a 64 bit Java SDK into a directory with no spaces and no funky characters.  After the installation was complete, I had to quickly update the JAVA_HOME environment variable so that it was pointing to my new installation location, open a command prompt and run ’set’ to see if my new JAVA_HOME value was recognised and then ‘java -version’ to make sure that the right version of Java was on the path and working.  I then started up HTML5 Builder and went through the Deployment Wizard and Robert’s your Fathers Brother, both the deploy against local SDKs and Phonegap:Build were available and my JAVA_HOME warning had been sorted too.

Out. Damo.

A short novel on TBindList

June 29th, 2012 by damienbootsma

The research for this post has been a ton of fun.  I was puzzled as to how the TBindList did it’s thing so I have been digging around in the Delphi source code for answers and I also enlisted the help of one of our R&D Engineers to help me over the line (Thanks Jim!).  But enough emotion, lets construct the example.

The goal of this example is to populate a TListBox with a collection of ‘business objects’ (A TList of TDamo) and to use LiveBindings to link everything together.  Create a FireMonkey HD Application and add the following components to it:

TBindingsList (blDamoExpressions)
TBindScope (bsDamoCollection)
TListBox (lbDamoObjects)
TEdit (edtDamoObject)
TCheckBox (cbBindListEnabled)

My UI looks a little like this:

Save All and save the name of the form as FLiveBindingList and the name of the project as LiveBindingList.  Make a copy of UDamoObject.pas that we created in our last example, paste it into our new project directory and add it to the project by right clicking your project in the Project Manager window and selecting Add…  We will create one more unit that will manage our collection of TDamo.  Save this unit as UDamoObjectManager.pas and replace the existing code in the unit with the following:

unit UDamoObjectManager;
interface

uses Generics.Collections, UDamoObject;

type TDamoObjectManager = class
  private
    lDamoObjectList : TList<TDamo>;
  public
    Constructor Create();
    property DamoObjectList : TList<TDamo> read lDamoObjectList;
    procedure AddADamo(Damo : TDamo);
    function GetADamo(DamoIndex : Integer) : TDamo;
  end;

implementation

procedure TDamoObjectManager.AddADamo(Damo: TDamo);
begin
  lDamoObjectList.Add(Damo);
end;

constructor TDamoObjectManager.Create;
var
  TempDamoObj : TDamo;
begin
  lDamoObjectList := TList<TDamo>.Create();
end;

function TDamoObjectManager.GetADamo(DamoIndex: Integer): TDamo;
begin
  Result := lDamoObjectList.Items[DamoIndex]
end;

end.

As you can see from the code above, this unit contains a TList (lDamoObjectList) that will hold all of my TDamo objects, a property to expose my TList (DamoObjectList), a procedure to add an object to the list (AddADamo) and a function to return one of the objects from the list (GetADamo).

The next step is to create an OnCreate event for the form.  In this event handler we want to instantiate TDamo objects, add them to our TList and link the TList to our TBindScope (bsDamoCollection).  Select Form1 from the structure pane, click the Events tab in the Object Inspector and double click the OnCreate event.  Implement the event handler as follows:

procedure TForm1.FormCreate(Sender: TObject);
var
  Damo : TDamo;
begin
  DamoObjManager := TDamoObjectManager.Create();
  Damo := TDamo.Create('Damo');
  DamoObjManager.AddADamo(Damo);
  Damo := TDamo.Create('Jeanette');
  DamoObjManager.AddADamo(Damo);
  bsDamoCollection.DataObject := DamoObjManager.DamoObjectList;
end;

I don’t think that my wife would like to be classified as a TDamo, I’ll take that one on the chin when I get home :)

The next step is to now create a bind expression that will link our collection of business objects to the TListBox.  Go back to the Form Designer and double click blDamoCollection to open the LiveBinding Editor, click the new button and select the TBindList expression type.  Set the following properties:

ControlComponent: lbDamoObjects
Name: bndlstDamoList
SourceComponent: bsDamoCollection

Double click bndlstDamoList to open the Expression Editor.  You will notice that in the left hand navigation window there are three ‘Collections’.  These are the different types of expressions that we can create between our source and control objects in a TBindList:

FormatControl: Changes the look and feel of the TListBox itself based on data from the source.

ClearControl: Performs some user-defined functionality when the LiveBinding associated to the TListBox is no longer enabled.

Format: Modifies the contents of our TListBox.

So, just to clarify, the first two types of expressions are at the Control level whereas the third type is at the Item level of the Control.

We are going to add one of each expression to show how they work.  Select the FormatControl collection, click the new button and add the following to the source and control expressions:

Control Expression: Height
Source Expression: Count * 20

When the LiveBinding is active, the height of lbDamoObjects will be 20 multiplied by the amount of objects in the collection of TDamo objects.  (Probably not the best example to use if you have a shed load of TDamo objects, but again, it’s to show the concept of the FormatControl expression)

Select the ClearControl collection, click the new button and add the following to the source and control expressions:

ControlExpression: Height
SourceExpression: 180

Again, I’m focusing on the look and feel of the TListBox.  When the LiveBinding is no longer enabled, the TListBox will go back to the original height of 180.

Finally, let’s add a Format control.  Select the Format collection, click the New button and add the following to the Source and Control expressions:

ControlExpression: Text
SourceExpression: Current.Name

Run the application.  You should see the TListBox shrink to a size of 40 and the names of two TDamo objects should be displayed.

At this point, I think that there are a number of questions that need to be answered:

1. How am I iterating over my collection of TDamo objects? TBindScope takes care of this.  TBindScope descends from TCustomBindScope.  TCustomBindScope implements an Interface called IScopeRecordEnumerable which has one function called GetEnumerator.  Within TCustomBindScope, GetEnumerator creates a TEnumerableWrapper and our DataObject (TList of TDamo) is passed as a parameter to the TEnumerableWrapper constructor.  TEnumerableWrapper then uses RTTI against our DataObject to access it’s contents.

2. How are the TDamo objects being added to the TListBox? A TBindListListBoxEditor is created and supports the necessary methods and functions to traverse and access data in a TListBox.  The Fmx.Bind.Editors unit contains the implementation and registration of this class for our FireMonkey TListBox.  TBindList descends from TCustomBindList.  TCustomBindList has a function called TryGetBindListEditor.  This function will use a factory to create a TBindListListBoxEditor based on our TListBox and the IBindListEditor interface.

3. Where do the Current and Count properties come from? The Current property is made available through the IScopeRecordEnumerator Interface that is provided by the TBindScope.  I believe that the Count property is made available directly from the TList, which is our DataObject.  (Like the Name property of TDamo that we exposed directly through a TBindScope in the last post)

4. Can I fit all of this into one blog post? Yes, but it will be a short novel :)

I have added a couple of other LiveBindings to this example which are worth looking at too.  The first is a way of taking items from our TListBox and setting them in a TEdit.  Double click blDamoExpressions, create a new TBindExpression and set the following properties:

ControlComponent: edtDamoObject
ControlExpression: Text
Name: bxTDamoNameToEdit
SourceComponent: lbDamoObjects
SourceExpression: Selected.Text

When a user of the application clicks one of the items in the TListBox, the name of the item selected will be displayed in edtDamoObject.  In order to complete this LiveBinding, we need to add a Notify call in the OnChange event handler of lbDamoObjects.  Select lbDamoObjects in the Object Inspector, double click the OnChange event handler and add the following line of code:

blDamoExpressions.Notify(Sender, '');

Run the application and select one of the items in the TListBox and it should be displayed in the TEdit:

Our final LiveBinding will control whether our TBindList binding is enabled or disabled.  To do this we will use the IsChecked property of the TCheckBox as our source.  Create a new TBindExpression and set the following properties:

ControlComponent: bndlstDamoList
ControlExpression: Active
Name: bxEnableLiveBinding
SourceComponent: cbBindListEnabled
SourceExpression: IsChecked

Before you run this example, make sure that the AutoActivate property of bndlstDamoList is false and the IsChecked property of cbBindListEnabled is also false.  We also need to add an OnChange event handler to cbBindListEnabled and make a Notify call:

procedure TForm1.cbBindListEnabledChange(Sender: TObject);
begin
  blDamoExpressions.Notify(Sender, 'IsChecked');
end;

When run, you should see an empty TListBox.  Check cbBindListEnabled and the TBindList should become active and populate our TList.

If you would like the source code, drop a comment and I will get it out to you.

Out. Damo.

Providing Direction to LiveBindings

June 25th, 2012 by damienbootsma

In my last couple of posts I have focused on one-way livebindings - updating a ‘control’ object when a ’source’ object changes - but what about bidirectional livebindings I hear you ask…

In this example, I am going to use a couple of TBindExpressions to demonstrate how a bidrectional livebinding works.  To kick things off, create a new FireMonkey HD Application and add the following controls to it:

TEdit: Name := edtObjectName
TEdit: Name := edtDamoObjStatus
TBindingsList: Name := blDirectionDemo
TBindScope: Name := bsDamoObj

My UI looks a little like this:

The goal of my example is to have a ‘Business Object’ (TDamo) be the source of my data.  I want to be able to populate a TEdit (edtObjectName) and I also want to give edtObjectName the ability to write back to my instance of TDamo.  I am using the second TEdit (edtDamoObjStatus) as proof that the liveBinding is indeed working.

All of the controls that I have set above are all familiar except for the TBindScope (bsDamoObj).  This component is useful as the object that I associate to it (via it’s Component property) and its children become available to be either source or control objects.  For example, I could refractor our last project that usedTBindExprItems  by adding a TBindScope to the form and setting its Component property to Form1.  I could then update my control expressions from Owner.<Object>.<Property> to <Object>.<Property> and make them a little more readable.

The next thing to do is to create a new unit to hold our TDamo class.  Create a New Unit (File | New | Other | Delphi Files | Unit), add the following declaration and implementation and save this as UDamoObject.pas:

interface

type TDamo = class
  private
    sName : String;
  public
    property Name : String read sName write sName;
    constructor Create(Name : String);
end;

implementation
{ TDamo }
constructor TDamo.Create(Name: String);
begin
  sName := Name;
end;
end.

Nice and simple.  Now, let’s create an OnCreate event for Form1 and in that event, create an instance of TDamo and assign it to the DataObject property of bsDamoObj (I have declared DamoObj as a global variable).

In this example, because TDamo doesn’t descend from TComponent, we don’t set the Component property of bsDamoObj, instead we will set the DataObject property on bsDamoObj.

procedure TForm1.FormCreate(Sender: TObject);
begin
  DamoObj := TDamo.Create('DamoObject');
  bsDamoObj.DataObject := DamoObj;
end;

Flip back to the Form Designer and we will add our TBindExpressions.  Double click on blDirectionDemo to open the LiveBinding Editor, click the New button, and choose TBindExpression.  Select your TBindExpression and set the following properties:

Name := bxDamoObjName
ControlComponent := edtObjectName
ControlExpression := Text
Direction := dirBidirectional
NotifyOutputs := True
SourceComponent := bsDamoObj
SourceExpression := Name

The first thing to note here is the Direction property.  This is where we can set the behaviour of the expression.  There are three options:

dirSourceToControl: The standard, one-way flow of data from source to the control object

dirControlToSource: One-way flow from the control back to a source object

dirBidirectional: The source and control objects can pass data back and forth between one another

If someone has a good reason to use dirControlToSource, please let me know!

The NotifyOutputs property is the second thing to notice.  If we have another expression where the source object of that expression is the same as the current expression and we want that other expression to fire then NotifyOutputs needs to be enabled.  In our example, we will create another expression that will display the name of our TDamo object in edtDamoObjStatus to prove that the bidirectional expression works but this expression won’t fire if NotifyOutputs on our first expression is false - to clarify, the source object, DamoObject will be updated, but the actual expression that displays the name in edtDamoObjStatus won’t fire automatically.

Third and finally, you will see that our SourceComponent is set to bsDamoObj.  This is the TBindScope which is associated to our TDamo object.  Because of this, I can simply set SourceExpression to Name (the property of our TDamo object).

In the LiveBinding Editor, click New again to create our second TBindExpression.  This is the expression that will be a typical Source-To-Control TBindExpression and its properties are set as follows:

Name := bxDamoObjStatus
ControlComponent := edtDamoObjStatus
ControlExpression := Text
SourceComponent := bsDamoObj
SourceExpression := Name

Close the LiveBinding Editor so that we are back to the Form Designer.  All that is left now is to notify the application when a change is made to our TDamo object.  This will happen when we execute an OnChange event in edtDamoObjName.  Create an OnChange event for edtDamoObjName and add the following line of code to it:

blDirectionDemo.Notify(Sender, 'Text');

Run the application and you should see both TEdit controls displaying the same name.  Make a change to the top TEdit and you should see the second TEdit reflect that change.  I also encourage you to play around with the NotifyOutput property and see how that affects your other LiveBindings.

Out. Damo.

Express yourself further with TBindExprItems

June 19th, 2012 by damienbootsma

If a data element or a UI control changes state in your application, you may want multiple actions to occur.  For example, when I change tabs in a TTabContol, I want a number of UI controls to also change - this is the perfect job for a TBindExprItems LiveBinding.  This component groups ‘linked’ expressions together and executes them.

Let’s create a new FireMonkey HD Application, Save All (FDamoExprItems.pas and ExprItemsDemo.dpr) and add some controls to see this behaviour:

In your application add the following UI controls and lay them out to something similar as follows:

- TTabControl: Name:= tcTabDemo, add 5 TTabItems to tcTabDemo using the ‘Add Item’ link at the bottom of the Object Inspector

- TEdit: Name := edtTabStatus

- TProgressBar: Name := pgTabStatus, max := 4

- TTrackBar: Name:= tbZoomy, max := 4

With our UI controls laid out on the form, it is now time to create our LiveBinding.  The functionality that I want to implement is when the user switches to a new tab, I want edtTabStatus to display the Tab Index that is currently selected, pgTabStatus to reflect the Tab Index that is currently selected and tbZoomy to also display the currently selected Tab Index.  From the Tool Palette, find and drop a TBindingsList on the form (Name := blExpressionItems) double click to display the Expression Editor, click the New icon in the top left hand side of the Expression Editor and choose TBindExprItems and click Ok.

The first thing that we need to set up is the Source and Control objects.  In our scenario I want to capture the current TabItem that is selected so the source object is tcTabDemo.  I’m going to have multiple control objects so for now I will set the control object as edtTabStatus.  We set these by selecting our LiveBinding in the open editor and from the Object Inspector set the ControlComponent and the SourceComponent.

With the Expression Editor still open double click your newly created TBindExprItems (Name := bxiExpressionItems) so that we can add our expressions.  You will notice a slight difference between this editor and the TBindExpression editor that we looked at previously.  On the left side of the Editor we have two ‘types’ of expressions that we can create - Format expressions and Clear expressions.  Format expressions involve displaying our source data in our control object.  Clear expressions also format our control object but will only get called when the LiveBinding is deactivated.

We will create the Format expressions first.  Make sure that the Format type is selected on the left hand side of the Expression Editor, click the New button in the top left hand corner and set the Control Expression as Text and the Source Expression as "Tab Index: " + ToStr(TabIndex).  What we are basically doing here is converting the TabIndex property of tcTabDemo to a String and then appending it to the static string "Tab Index: " and displaying this in the Text property of edtTabStatus.

Create a second Format expression, set the Source Expression to TabIndex and set the Control Expression to Owner.tbZoomy.Value.  This is really cool!  With my expression, I can use the Owner property of edtTabStatus and then access the Value property of tbZoomy.

Create a third and final Format expression by setting the Source Expression to TabIndex and the Control Expression to Owner.pgTabStaus.Value.  We have now created three Format expressions all based on the single source of TabIndex from tcTabDemo.

Let’s create a Clear expression to show how these type of expressions work in an application.  When the LiveBinding is not active, I want edtTabStatus to display the text ‘0′.  Select the Clear type in the left hand side of the Expression Editor, click the New button in the top left hand corner and set the Source Expression to 0 and the Control Expression to Text.

Close all of the editors so that we are back to the Form.  Now we need to trigger this LiveBinding.  As from our first examples, this LiveBinding will fire when the source object changes, in this case, tcTabDemo.  Create an OnChange event handler and enter in the following line of code:

blExpressionItems.Notify(Sender, 'TabIndex');

Save what we have implemented and run the application.  When you select a TabItem, edtTabStatus, pgTabStatus and tbZoomy should all reflect which TabItem you have selected.

Finally, let’s see the effect of the Clear expression that we created.  Create an OnCreate event for your form and add the following line of code:

bxiTabControl.Active := false;

This will deactivate our LiveBinding when the application starts.  In order to enable the LiveBinding add a TCheckBox to the first TTabItem page (Name := cbEnableBinding), create an OnChange event and add the following lines of code:

if cbEnableBinding.Enabled then
  bxiTabControl.Active := true

Rerun the application and notice that edtTabStatus now contains the value ‘0′.  Flick through the tab pages and you will see that noting happens.  Jump back to the first TabItem and enable cbEnableBinding - you should now see that the LiveBinding is active and working as expected.

In wrapping up, I hope that you can see that TBindExprItems builds on the simple TBindExpression by providing the ability to have more than one control object involved when the source object is modified and also the ability to clear the contents of our control objects when the binding is no longer active.  I hope that you can also see that from a coding perspective we have simplified our UI code by abstracting the logic required to associate data and/or state changes to and from our UI objects.  This flexibility means that I can add and remove control objects without disrupting any of the UI code.

Out. Damo.

Implementing a BindExpression in Code

June 13th, 2012 by damienbootsma

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.

Kicking off with LiveBindings

June 6th, 2012 by damienbootsma

I plan on writing a series of posts that deal with LiveBindings.  I’ll start right at the beginning and continue to work through all of the capabilities that LiveBindings provide.  This post is going to explain LiveBindings, introduce some of the vernacular that we use and run through a simple demonstrate to illustrate these concepts.

LiveBindings is a mechanism that allows you to expose data from one object and make it available to another object (or the same object, just assigning that data to another property).  The way that we connect and make the data available is through an expression.  An expression, in it’s simplest form is a ‘formula’ in which you specify the object and property that contains the data you want to share (the Source Object) and the object and the property that you want to consume that data (the Control Object).

For our demonstration, I want the Value of a TTrackBar to be displayed in the Text property of a TEdit.  I’ll build a Delphi HD FireMonkey application for this demonstration but you could also use the VCL, FireMonkey 3D project types too (Delphi or C++ Builder)

1. Grab your TTrackBar (set Name := tbZoomy) and TEdit (set Name := edtZoomyValue) and put them on your form:

2. Search the Tool Palette for a TBindingsList (set Name := blSimpleExpression) and drop it on your form.  TBindingsList is a non-visual component and it keeps track of all your LiveBindings.

3. Create the new LiveBinding.  Double click blSimpleExpression so that the LiveBinding Editor is displayed.  Click the New icon in the top left hand corner of the LiveBinding Editor to create a new Binding.  Select the TBindExpression and click Ok.  This is the simplest LiveBinding that will expose data from one property from our Source Object and make it available to a property in our Control Object.

4. Back in the LiveBinding Editor, select BindExpression1 and change the name in the Object Inspector to beTrackBarValueEditText.  While we are in the Object Inspector, set the Source Component to tbZoomy and the Control Component to edtZoomyValue.

5. So we’ve created our LiveBinding and we have set the Source and Control Objects for our Expression.  We now need to set the Source and Control Properties.  There are a couple of places where you can set this and I’ll show you one of them.  Double click on beTrackBarValueEditText in the LiveBinding Editor to display the Expression Editor.  Set the Source Expression to Value and click the tick so that it adds this to our Expression.  Set Text as the Control Expression and click the tick too.

So that’s it!  We have now created our LiveBinding by defining a simple expression that takes the value of tbZoomy and sets it in the text property of edtZoomyValue.  Close the Expression Editor and the LiveBinding Editor.

How do we trigger the LiveBinding?  How does the Expression Engine know when to execute the expression?  I want this expression to be evaluated each time that I slide tbZoomy so in this instance I will create an OnChange event handler for the tbZoomy and notify the Expression Engine that the value property has changed.

1. Select tbZoomy, select the Events tab in the Object Inspector and create an OnChange event handler

2. Complete the event handler with the following line of code so that will notify the BindingEngine that our Value property has changed

3. Run your application up, start moving tbZoomy and see the value displayed in edtZoomyValue

So you might be asking yourself "Ok, so what benefit does LiveBindings give me?"  Granted, it’s a little hard to realise the benefits with such a simple example but one benefit that I hope you see is that we have taken the assignment logic that is typically in our event handler and abstracted it out to our TBindingsList component.  Our event handler only contains a notification that a property has changed and it’s effectively decoupled from having to assign that value to other objects.  I can easily change the control object (or add others) and not affect the underlying source code.  We will see this benefit of LiveBindings more clearly plus other benefits as we create more complex expressions.

So thats all for now, next time I will write up how you can manage multiple control objects from a source object, bidirectional and unidirectional bindings

Out. Damo.


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

Close