Delphi XE2 LiveBindings

Delphi's original design permitted two or more components to be associated with each other through a mechanism specific to those components. For example, a Label could be associated with an input field, such as an Edit, through the Label's FocusControl property. If you then included an accelerator in the Label's Caption property (for example, &Name, where the N becomes underlined in the Caption indicating that Alt-N is the accelerator), the user can set focus to the specified control by pressing the accelerator key.
 
Another familiar example is the classic data aware control mechanism employed by Delphi. Data aware controls internally use descendants of the TDataLink class to associate the control with a DataSet, and in many cases, a specific field of that DataSet.
 
What both of these examples have in common is that they are entirely customized. They are designed for the specific purpose of associating controls for a particular purpose. A Label cannot use the FocusControl property to link to a DataSet field, and a data aware control cannot use its DataLink for enabling accelerator keys.
 
Some of the more modern components libraries, such as the .NET Framework Class Library (FCL), support generic object binding mechanisms, whereby just about any property of any class can be bound to any other. As result, Delphi's linking mechanism felt limiting and obsolete.
 
While Delphi's custom component linking is still present in the VCL, FireMonkey, Delphi's new, cross-platform component library, introduced a generic binding mechanism in tune with modern component-based development. Delphi calls this mechanism managed LiveBindings.
 
LiveBindings are extremely flexible, but has some significant complexities. To make matters worse, the documentation for LiveBindings is limited, and entirely missing in some cases.
 
In this article I am going to provide you with an introduction to LiveBindings. I'll start with a discussion of managed versus unmanaged LiveBindings, and demonstrate the use of each.
 
But before I begin, I do have a disclaimer. I am still leaning LiveBindings. There is a lot that I do not know yet, and a lot of this is largely undocumented. I hope that I've captured the essence of LiveBindings well in this first attempt.

Overview of LiveBindings

LiveBindings is a general term for Delphi's new object/property binding mechanism first introduced in RAD Studio XE2. It is the only binding mechanism available to the new FireMonkey cross-platform component library (FMX), and is also available for traditional visual component library (VCL) components.
 
There are two distinct types of LiveBindings: Managed and unmanaged. Managed bindings can be used to link an object to an expression, which makes it ideal for linking a property on one object to almost anything. Managed LiveBindings are similar to the bindings available in the .NET framework, though they are significantly different in their implementations.
 
Unmanaged bindings share a lot in common with Delphi's traditional component binding I described in the introduction to this paper. Unlike managed LiveBindings, unmanaged LiveBindings are designed to connect specific components for specific purposes. For example, one type of unmanaged LiveBinding component, the BindScopeDB, is used to connect a component to a DataSet
 
LiveBindings is a mechanism for creating associations between objects and expressions
 
At its core, LiveBindings is a mechanism for creating associations between objects and expressions. Expressions are strings that are compiled and evaluated by the LiveBinding expression engine, and they define what effect the expression will have on the associated object.

Expressions

While expressions are strings, they are compiled and acted upon by the expression engine at runtime, as opposed to being compiled by Delphi's compiler. As a result, they are different from other string types you normally encounter in Delphi code. For one thing, expression strings can define literal values using either single quotes or double quotes. In addition, the expression engine recognizes special methods available for use in expressions, and can employ custom output converters to transform data. More will be said about these methods and output converters later in this article.
 
Each LiveBinding has four elements. These are the control object, the control expression, the source object, and the source expression. The control object is the one to which the expression is associated, and the control expression is a property of that object. For example, if you want to bind the Text property of a FireMonkey Label component to the Text property of an Edit component, the control object is a reference to the Label and the control expression is Text.
 
The source object is another object, often one whose property you want to bind to the control expression. When using LiveBindings for visual components whose properties you want to synchronize, the source object will be one of the visual components. The source component might even be the control component, if you want to synchronize two properties on a single object. In other cases, the source object might be one of the provided classes in the LiveBindings framework. For example, it might be a BindScope or a BindingsList.
 
The source expression is one of the specially constructed strings that can be consumed by the expression engine. In the simplest case, the source expression is a property of the source object. However, the source expression can also include string literals (using either single or double quotes), references to properties of the source object, references to properties of other objects visible to the source object, operators, and special methods registered to the expression engine.
 
The expression engine evaluates the source expression when it is notified that a change has occurred in the source control, and assigns the value of the source expression to the property defined in the control expression.

Managed LiveBindings Revisited

Here is where the big difference between managed bindings and unmanaged bindings becomes apparent. Managed bindings require that the expression engine by notified that a change has occurred, and this does not happen automatically. With managed bindings, you must add additional code to one or more event handlers that trigger in response to changes that affect your bindings, and you notify the expression engine from these event handlers.
 
If you add the System.Bindings.Helper unit to your uses clause, you can use the TBindings class to call the class function Notify, to which you pass a reference to the source component, and optionally a string that contains the name of the property affected and a BindingsManager reference. When you omit the property name in the second parameter, all expressions linked to the specified source component are evaluated. If you omit the BindingsManager in the third parameter the default BindingsManager, named AppManager, is used.
 
Instead of using the TBindings class, you can call the Notify method of a BindingsList, which takes the same parameters. When you create your bindings at design time, a BindingsList is added to your project automatically, so this is often the route you will take.

Some Hands-On Examples

It is hard to grasp LiveBindings until you start using them. So, let's begin by creating a basic LiveBinding based on a BindExpression. These steps walk you through creating a FireMonkey HD Application, but you could use almost the same techniques with a VCL application.

Using BindExpressions

Use the following steps to create a new FireMonkey Application and add LiveBindings.
 
From Delphi XE2's main menu, select File | New | FireMonkey HD Application.
 
Add two Edit controls from the Standard page of the Tool Palette.
 
Right-click Edit2 and select New LiveBinding….
 
From the New LiveBinding dialog box displayed in Figure 1, select TBindExpression and click OK. (If you are adding a LiveBinding to a VCL Application, the New LiveBinding dialog box will have fewer entries.) The new BindExpression will be selected in the Object Inspector with Edit2 selected as the control component, and a new BindingsList has been added to your form.
 
 
Figure 1. The New LiveBinding dialog box
 
Using the Object Inspector, set ControlExpression to Text (the Text property of Edit2), set the SourceComponent to Edit1, and set the SourceExpression property to the following string:
 
"The Form " + Owner.Caption + ' says ' + Text
 
Owner in the preceding expression is referring to the Owner property of Edit2. Since Edit2 has been placed directly on the form, Owner is the form. We cannot use Self in this case, since the variable Self is visible from a method of the form, but not from the expression that is evaluated by the expression engine. In addition, Text in this expression is the Text property of Edit1, the source control.
 
All you need to do now is to inform the expression engine when the Text property of Edit1 changes. Select Edit1 and double-click the OnChange property from the Events tab of the Object Inspector. Add the following one line of code to this event handler:
 
BindingsList1.Notify(Sender, '');
 
That's it. Run the project and enter Delphi Forever! in Edit1. If you then press the Enter key or change focus off this control the OnChange event handler triggers, and the LiveBinding updates the contents of Edit2, as shown in Figure 2.
 
 
Figure 2 The BindExpression is updating Edit2 each time Edit1 changes

Creating BindExprItems

While BindExpressions give you a lot of flexibility, BindExprItems are often a better solution when you want to link visual controls. In this next example you will add four components to this project that will stay synchronized: A ProgressBar, a TrackBar, an Edit, and a Label.
 
The control component will be a ProgressBar, which will be updated when the Value property of a TrackBar is changed. Furthermore, an Edit and a Label will also be updated, and the Edit control will provide bi-directional control of the TrackBar.
 
Begin with the sample project you created in the last example.
 
From the Standard page of the Tool Palette, add to your form one Label, one Edit, one TrackBar, and one ProgressBar.
 
Right-click the ProgressBar and select New LiveBindings…. From the New LiveBindings dialog box, select TBindExprItems and click OK.
 
As before, the ControlComponent property of the created LiveBinding is set to the object you selected, which is the ProgressBar in this case. Using the Object Inspector, set the SourceControl property to TrackBar1.
 
This time we'll use the BindingsList to continue the configuration of the BindExprItems. Double-click the BindingsList to display the bindings collection editor, and then double-click the BindExprItemsProgressBar11 binding that appears in the Bind Components pane. Delphi will display the dialog shown in Figure 3.
 
 
Figure 3: You can use this dialog box to configure a BindExprItems.
 
Begin by selecting Clear in the Collections list, and click the Add button (or press Ins). This adds a new ClearExpressions ExpressionItemDir to your BindExprItems. Set Control expression for ProgressBar1 to 0 (zero), and Source expression for TrackBar1 to Value. With Clear[0] still selected in the Expressions pane, use the Object Inspector to set the Direction to dirControlToSource.
 
(Note: I have to admit that I don't know what Clear does yet, as it is not documented, but the many examples that ship with Delphi implement a Clear ExpressionItemDir, which is why I am doing so here.)
 
Next we have to add one or more Format ExpressionItemDir elements. These are the expressions used to update properties when changes occur.
 
Select Format from the Collections pane, and add a new Format ExpressionItemDir. Set Control expression for ProgressBar1 to Value, and Source expression for TrackBar1 to Value.
 
Add another format collection item, and set Control expression for ProgressBar1 to Owner.Edit3.Text and Source expression for TrackBar1 to Value. While this ExpressionItemDir is selected, use the Object Inspector to set the Direction property to dirBidirectional.
 
Add a third format collection item. Set Control expression for ProgressBar1 to Owner.Label.Text and Source expression for TrackBar1 to Value. When you are done, the dialog box should look something like that shown in Figure 4.
 
 
Figure 4 The Newly Configured BindExprItems
 
You can now close the two BindingsList dialog boxes.
 
Like the BindExpression class, a BindExprItems is a managed LiveBindings, which means that we must inform the expression engine when it needs to evaluate the expressions. Begin by selecting the TrackBar1 control on the form. Using the Object Inspector, create an OnChange event handler. Complete the event handler as shown here.
 
procedure TForm5.TrackBar1Change(Sender: TObject);
begin
 BindingsList1.Notify(Sender, '');
end;
 
We also need to inform the expression engine when the Edit is updated. Select Edit3 on the form, and set its OnChange event property to TrackBar1Change.
 
You can now run the form. As shown in Figure 5, when you either change the position of the TrackBar or enter a value between 0 and 100 in the Edit, all four of these controls stay synchronized.
 
 
Figure 5. A BindExprItems class keeps four controls synchronized

An Unmanaged LiveBinding Example

This last example demonstrates the use of an unmanaged LiveBinding. While you might think that an unmanaged LiveBinding requires more custom code from you, the opposite is actually the case. Unmanaged bindings often require little or no additional code, since the objects themselves communicate about changes that occur.
 
In this example, we are going to bind some controls to a DataSet, specifically a ClientDataSet.
 
Starting from the project created earlier in this article, add a DataSource and a ClientDataSet from the Data Controls page of the Tool Palette. Then, from the LiveBindings page of the Tool Palette add a BindScopeDB component and a BindNavigator component.
 
Finally, add a Label from the Standard page of the Tool Palette, and a StringGrid from the Grids page of the Tool Palette. Adjust the position of the components to look something like that shown in Figure 6.
 
 
Figure 6. Preparing to connect to a ClientDataSet
 
Set the ClientDataSet's FileName property to the Customer.cds file in Delphi's sample directory. In Delphi XE2, this directory should default to C:\Users\Public\Documents\RAD Studio\9.0\Samples\Data. (If you cannot find it, select Start Menu | All Programs | Embarcadero RAD Studio XE2 | Samples. Customer.cds is in the Data subdirectory.) Then, set the ClientDataSet's Active property to True.
 
Next, set the DataSet property of DataSource1 to ClientDataSet1. Also, set the DataSource property of the BindScopeDB to DataSource1.
 
The hard part is done. Now, right-click the StringGrid and select Link to DB DataSource. Select BindSourceDB from the displayed dialog box, and instantly the StringGrid will show the data from the ClientDataSet.
 
Next, right-click the Label (Label2) and select Link to DB Field. Select Company from the displayed dialog box. The Label responds by display the company name for the first record in the active ClientDataSet.
 
Finally, select the BindNavigator and set its BindSource property to BindSourceDB1.
 
If you now run this project, your screen should look something like that shown in Figure 7.
 
 
Figure 7. Unmanaged bindings link FireMonkey controls to a DataSet

You can navigate the records of the ClientDataSet using the BindNavigator, and you can even edit the data directly in the StringGrid. In fact, this general approach of using a BindSourceDB component permits you to link almost any type of visual control to a DataSet.

Methods and Output Converters

I have mentioned methods and output converters only in passing, but it is important to consider these two elements. Delphi ships with a collection of methods that can be called by the expression engine. To view these methods, select the BindingsList component and then click the ellipsis button next to the Methods property in the Object Inspector. Delphi responds by displaying the Methods dialog box, shown in Figure 8.
 
 
Figure 8. The Methods dialog box
 
The Methods dialog box lists all of the methods that have been registered with the expression engine. As described earlier in this article, you can use these methods from your source expression. Furthermore, it is possible to write and register custom methods with the expression engine, though that topic is beyond the scope of this article.
 
Output converters, like the Methods, are routines designed to perform transformations on data. To view the output converters available to the expression engine, select the BindingsList component and then click the ellipsis button next to the OutputConverters property in the Object Inspector. The Output Converters dialog box is shown in Figure 9.
 
 
Figure 9. The Output Converters dialog box.
 
Like methods, it is possible to write custom output converters that you can then register with the expression engine.

Code

The project used in this article can be downloaded using the following URL:
 
 
This download also includes a VCL version of this project. The VCL version does not include the unmanaged StringGrid example, as the VCL currently lacks the Link to DB DataSource component editor simplifies the linking of the grid to the BindScopeDB. As a result, most VCL applications that require a grid will likely continue to use the traditional DataSource linking. Also, there are slight differences in the properties used to configure the managed LiveBindings.

Summary

LiveBindings provide you with a flexible mechanism for associating an object with one or more expressions that can update properties of the object. This article has demonstrated some examples of how to use both managed and unmanaged LiveBindings. Hopefully these examples will inspire you to further explore this sophisticated new feature of Delphi XE2.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar