RAD Studio XE2 introduces a new mechanism for updating the user interface of applications in Delphi and C++Builder applications. This technology, which complements, but does not replace, the event driven model already provided by RAD Studio, enables developers with new and creative options for updating the user interface as data changes. The data affecting these changes may be associated with a database, permitting applications to view and edit this data, but just as easily can be associated with the changing state of objects used within the application.
Overview of LiveBindings
LiveBindings is a mechanism in RAD Studio for associating the properties of objects with expressions. These expressions can evaluate properties of the same object, properties of other objects, or complex expressions that include properties, literals, operators, and methods. Unlike traditional event handlers, which are custom code blocks that are invoked by the internal methods of objects, LiveBindings are string expressions whose runtime evaluation is invoked in response to a change in the state of an object.
LiveBindings play a crucial role in FireMonkey development, providing a general mechanism for binding user interface controls to DataSets. This is important since RAD Studio does not provide a collection of data-aware FireMonkey controls, as it does in the VCL (visual component library). Instead, LiveBindings permits any of FireMonkey's user interface controls to display data, and the same can be said about many of the VCL's non-data-aware controls, which can now also be bound to DataSets using LiveBindings.
On a broader level, however, LiveBindings provide RAD Studio developers with options not available in previous versions of RAD Studio, and serve as a basis for a new model of user interface synchronization. Similar to, but different than, data binding found in .NET, LiveBindings demonstrate Embarcadero's commitment to providing RAD Studio developers with leading edge technologies for software development.
An example of a LiveBinding is one that synchronizes two Edit controls, user interface objects that permit keyboard user input. One Edit may permit the user to enter the cost of an item in US dollars, and the other in euros. As the user enters the cost in dollars into the first Edit, the second Edit may update its display, displaying the equivalent cost in euros. The same LiveBinding can ensure that if the user enters the cost in euros into the second Edit, the first Edit updates its display in the equivalent dollars.
The conversion from dollars to euros can be performed by one expression associated with a LiveBinding, and the conversion from euros to dollars can be performed by another expression performed by that same LiveBinding. Nonetheless, the LiveBinding serves to keep the two Edit controls synchronized with each other, without involving an event handler or method that has any specific knowledge the Edit or of currency conversion.
The Expression Engine
Central to RAD Studio's support for LiveBindings is the expression engine introduced in RAD Studio XE2. The expression engine interprets string expressions at runtime, evaluating those expressions to a data type appropriate to the expression. While these strings are typically defined by the developer at design time, they can also be defined at runtime, which provides a level of flexibility previously unavailable.
Here is an example of a string expression:
'Record ' + ToStr(RecNo) + ' of ' + ToStr(RecordCount)
This particular expression is associated with a LiveBinding that is described in the RAD in Action white paper Understanding RAD Studio's LiveBindings. The object that is affected by this LiveBinding is a StatusBar component, a control that you can use to display information to the user, and which is typically displayed at the bottom of a Window. More specifically, this LiveBinding is designed to assign the value of the preceding expression to the SimpleText property of the StatusBar. The following figure shows the resulting expression in the running application.
A little more background is required before the preceding expression makes sense. LiveBindings often involve at least two objects, an output object, or ControlComponent, which is an object with two or more properties that will be assigned the value of one of more expressions. In this example, this object is a StatusBar, and the property being assigned is the StatusBar's SimpleText property.
The second object is the input object, or SourceComponent, and the properties and methods of this object can be combined with operators and literals to create one or more input expressions. In this case, the input object is a BindScopeDB, an object that exposes the RAD Studio's DataSet interface to the expression engine. RecNo is a property of the DataSet to which the BindScopeDB points, and it represents the position of the current record within a table of data. Likewise, RecCount is a property of the DataSet, and it identifies how many records are in the table of data. You can see the product of this expression in the StatusBar shown in the preceding figure.
This LiveBinding is somewhat simple, in that it displays read-only data. The StringGrid control, by comparison, is far more involved. An example of a StringGrid appears in the lower-right corner of the preceding figure, and the data displayed by that StringGrid is managed using a LiveBinding. This particular LiveBinding involves almost a dozen expressions, and these expressions work together to keep the StringGrid synchronized, bi-directionally, with a DataSet, which is a ClientDataSet in this particular example. This LiveBinding is an unmanaged LiveBinding, and no event handlers are needed to keep the data in the StringGrid synchronized with the DataSet.
A New Perspective
The LiveBindings that update the StatusBar and StringGrid represents a new way of synchronizing components in RAD Studio. Previously, the StatusBar would be updated by an event handler, such as the OnDataChange of a DataSource that points to the DataSet being displayed in the preceding figure. In that case, the event handler might look something like that shown in the following code:
procedure TForm1.OnDataChange(Sender: TObject); begin StatusBar1.SimpleText := 'Record ' + IntToStr(ClientDataSet1.RecNo) + ' of ' + IntToStr(ClientDataSet1.RecordCount); end;
While this event handler produces the same effect, updating the text displayed in the StatusBar, there is a very big difference between how this event handler performs the task and how it is done by the LiveBinding. In the case of the event handler, the event handler has to specifically name the object (the StatusBar) that is being affected, and to perform the assignment. If some other object, say the title of the form shown in the preceding figure, needed to be changed, the event handler text would have to look very different.
The LiveBinding used by the StatusBar is also an unmanaged LiveBinding, and it requires no event handler to perform its task. When the current record of the DataSet to which the BindScopeDB refers changes, the expression engine is called by the LiveBinding and the StatusBar's SimpleText property changes. The BindScopeDB knows nothing about the StatusBar. The StatusBar knows nothing about the BindScopeDB. The LiveBinding knows only those objects that it points to, and the expressions that are involved. And, it just works.
Another distinction between the OnDataChange event handler and the LiveBinding is that the event handler is compiled to binary code at compile time, and once compiled, will perform only those tasks whose need was anticipated by the developer at design time. The event handler is rigid, and any flexibility that is needed must be programmed into the event handler, leading to greater complexity.
LiveBindings perform their task by evaluating a string at runtime. This enables a whole new level of flexibility previous unavailable. For example, it is possible for the LiveBinding to obtain the string that it evaluates at runtime from the user interface, a database, or even from a service in the cloud. This new string could then be evaluated at runtime, permitting the LiveBinding to dynamically change the task it performs.
As mentioned, this LiveBinding is what is known as an unmanaged LiveBinding. As a general rule, unmanaged LiveBindings require no event handlers to work, relying instead on their implementation of the observer pattern, a technique in software development where one object registers its interest in another, so that the first object can stay informed about the second.
While the code to replace the StatusBar's display is easy to envision in code, the same cannot be said about writing event handlers to replace the LiveBinding used by the StringGrid. The StringGrid would require many, if not hundreds of lines of code to update its display, detect changes to its data, and write those changes back to the DataSet. There is simply no comparison between the simplicity offered by the LiveBinding and the complexity of the event handler approach.
There is another class of LiveBinding, called managed LiveBindings. Managed LiveBindings require that an object inform the expression engine every time something happens that requires that the expression be re-evaluated. These LiveBindings, while requiring an event handler, do not require that event handler to include any specific knowledge about what operations are being performed. They merely need to inform the expression engine that something has happened to an object, and these event handlers often look exactly like the following.
procedure TForm1.OnChange(Sender: TObject); begin BindingsList1.Notify(Sender, ''); end;
LiveBindings and Side Effects
The discussion to this point may have made it appear that LiveBindings and event handlers are mutually exclusive, but that is a gross oversimplification. Event handlers are compiled methods of an object, and those event handlers are executed by objects in response to a change in state or a user's interaction.
Just as objects can invoke methods within their scope, so can LiveBindings. Specifically, the string expression can include references to methods within the scope of the LiveBinding. These methods, being compiled code, execute very fast. In addition, these methods can do practically anything imaginable, from opening a database connection, to sending an email, to making extensive updates to the user interface.
These other tasks (sending an email, for example), when performed by accessor methods of properties (also referred to as property getters and setters), are referred to as side effects. Through this mechanism, changes to the property of an object may have, as an additional benefit, influences on other aspects of the object or the objects it uses. With LiveBindings, side effects can easily be extended beyond the object whose properties are being affected by the LiveBinding expressions. Specifically, while a method might return a value that is incorporated into the expression that the LiveBinding assigns to a property, that method can perform other tasks as a consequence of its execution. Side effects are an important feature of modern programming languages, and LiveBindings takes these effects to a whole new level.
RAD Studio XE2 introduces an important new technology that greatly expands the options that developers can use to manage the user interface. Not only do LiveBindings simplify the process of displaying database data, permitting almost any object to become data aware, they also permit dynamic expression evaluation that can produce significant side effects as a by product of the expression evaluation. This is but one of many new technologies that maintains RAD Studio's position as a leading software development tool.