Last week I introduced Delphi
RTL expression engine in the blog post at*
https://blog.marcocantu.com/blog/202...on-engine.html. Now let's make an additional step. I want to explain how components and their properties can be part of these expressions. In this case we don't simply evaluate an expression, but want to associate two separate expressions in a "binding".*
Binding Two Components
In practical terms, suppose you have a form with a SpinEdit and a ProgressBar. A managed binding allows you to provide an input expression (with one or more input objects) and an output expression (again with one or more objects). As you can see in the code below, each object is associate to the expression providing a name, like
spin1 for the SpinEdit1 component, and the expression can refer to the object and its properties, as in
spin1.Value. The same happens for the output. You can also specify an output converter, which I've omitted here:
BindingExpression1 := TBindings.CreateManagedBinding( { inputs } [TBindings.CreateAssociationScope([ Associate(SpinEdit1, 'spin1') ])], 'spin1.Value', { outputs } [TBindings.CreateAssociationScope([ Associate(ProgressBar1, 'progress') ])], 'progress.Position', {OutputConverter} nil); With this configuration code, if you call
BindingExpression1.Evaluate the value of the SpinEdit will be copied to the ProgressBar. You can make the same call to refresh the value, but you can also abstract the concept by indicating to the bindings architecture that the value property of the spin edit has changes. The system will automatically determine if there is one or more expressions involving the property and refresh it:
procedure TFormBindings.SpinEdit1Change(Sender: TObject);
</br> begin
</br> * TBindings.Notify(Sender, 'Value');
</br> end;
This change of perspective (a value has changed rather than an expression needs to be recalculated) is of a fundamental importance, as it fully abstracts the data model from the UI it is associated with. We'll get to it, but let me show the simple application UI first:
Binding Objects and UI Controls
This is better explained in the second part of the example, which has an edit box bound to an object in memory. The TMyObject*class has a Name and a Value property. Here is how you can bind an object*to the UI:
BindingExpressionObj := TBindings.CreateManagedBinding( { inputs } [TBindings.CreateAssociationScope([ Associate(MyObj, 'obj') ])], 'obj.Name', { outputs } [TBindings.CreateAssociationScope([ Associate(edName, 'edit') ])], 'edit.Text', {OutputConverter} nil); In this code MyObj is an object of the*TMyObject class. Its Name property is associate with the edName component Text. Again, refreshing the expression updates the output. But the object should have no knowledge of the user interface controls or the UI bindings. In fact, all you need to do to instrument the object is to notify the system that the value has changed:
procedure TMyObject.SetName(const Value: string); begin FName := Value; TBindings.Notify(self, 'Name'); end; With this code, when the data changes, the UI refreshes automatically to reflect it:
procedure TFormBindings.btnUpdateObjClick(Sender: TObject); begin myobj.Name := myobj.Name + 'Monkey'; end; I'm not 100% sure how you want to call the patterns implemented with bindings in Delphi, but it certainly offers a complete separation and abstraction of the data model with the user interface view.
There is Much More To Explore
Next, we can start looking into how you can work with bi-directional bindings, and how you can define all of the logic above using specific component designers, reducing the code required. There is a lot of technology behind Visual Live Bindings, which is the potential ultimate destination and what we often discuss. Live Bindings are nice, but the binding technology in the Delphi
RTL is where the power lives.
Weiterlesen...