Monday, February 27, 2012

LiveBinding Expression Can Produce Side Effects

I've been looking for a simple LiveBinding example that would forever change the way we perceive LiveBindings, and I think I found it. But let me start with a little background.

I've been interested in LiveBindings since RAD Studio XE2 shipped. One obvious reason is that LiveBindings is important if you want to easily bind your FireMonkey controls to traditional Delphi DataSets. But there was something else, something that I had a hard time putting my finger on.

I had a conviction, one that I shared publicly, that LiveBindings represented a fundamentally different way of doing things, and that we, as developers, just needed to see some examples of this new usage. Once we did, I thought, we would collectively come to a new way of looking at LiveBindings. These examples, however, have been elusive.

I remember David Intersimone (David I) asking me, prior to my 24 Hours of Delphi broadcast, to show LiveBindings used in a new and different way. I would have loved to, but I had yet to see such an example. I even blogged about this last month (http://caryjensen.blogspot.com/2012/01/whats-your-favorite-livebindings.html), asking readers to submit their favorite LiveBinding example that would open our eyes to the new way of doing things.

The response to that blog was interested. First of all, not one reader submitted a LiveBinding example. What I did get were some thoughtful comments about the limitations of LiveBindings, and these were similar to comments that I heard following my 24 Hours of Delphi presentation, as well as after a talk I gave at the Software Development Event in the Netherlands this past December.

Of these comments, the most common was that LiveBindings simply provide us with another way of doing something we already do, but in a more complicated way. Once again, I really felt like this opinion simply reflected the fact that we had not yet seen LiveBindings used in that way that would redefine, in our minds, what LiveBindings can do and how they can be used.

I recently wrote a white paper for Embarcadero on LiveBindings for their RAD in Action series. While doing so I had the time to reflect extensively on LiveBindings, including their limitations as they currently stand in this first release.

It was after I submitted the first draft for review that the example I am going to show came to me. In his technical edit, Jim Tierney pointed out that I failed to recognize that LiveBinding expressions could invoke methods of objects accessible in the input or output expression scope. I don't know why I overlooked this. He had mentioned this fact in one of his CodeRage 6 talks. I had simply forgotten. (And there is a trick. The method must uses parentheses, even if it has no parameters.)

As I re-wrote that particular section of the paper I had an inspiration. To be honest, this inspiration came at 2:00am in the morning, as inspirations often do. That pretty much ruined my night's sleep, as I could not wait to try what I now imagined a LiveBinding doing.

Here is the basic concept, and then I will show you a simple application. LiveBindings can produce side effects. That's it.

Sure, even this notion is not unique. I've seen several LiveBindings that assign a value to the ControlComponent's target property (the output expression), and that property has caused side effects (side effects being one of a property's magical features). But my idea, one that I had not seen before, was that you could invoke in your SourceComponent's SourceExpression (the input expression) a method that could, as part of its execution, produce a side effect.

Here is my simple example for your consideration. Take a look at this form, which is available in the code sample that accompanies my LiveBinding white paper.
Notice the Button on this form, the one whose Caption reads Close. This button, and another on another TabSheet of the PageControl, are associated with an ActionItem. This ActionItem has associated with it a BindExpression LiveBinding, which is a managed LiveBinding. Since it is a managed LiveBinding, there must be some code that triggers the expression engine to evaluate the LiveBinding expressions, and here is that code, associated with the ActionItem's OnExecute event handler.
procedure TForm1.Value1EditChange(Sender: TObject);
begin
  BindingsList1.Notify(Sender, '');
end;

Let's now look at the Object Inspector, which shows the properties of the BindExpression LiveBinding.

The ControlComponent and the SourceComponent properties are both set to the ActionItem. The ControlExpression (the output expression) is Caption, a property of the ActionItem. The magic occurs in the input expression (SourceExpression). In this expression is a method invocation, which I've associated with the form (the ActionItem's owner). This method, OpenCDS, produces a side effect, the opening and closing of a ClientDataSet, as you can see in the following implementation.

function TForm1.OpenCDS(Open: Boolean): string;
begin
  if Open then
  begin
    ClientDataSet1.Open;
    ClientDataSet1.LogChanges := False;
    exit('Close');
  end
  else
  begin
    ClientDataSet1.SaveToFile;
    ClientDataSet1.Close;
    exit('Open');
  end;
end;

As you can see, when the user clicks the button, the expression engine is notified to evaluate the LiveBindings associated with Sender, which is the ActionItem in this case. The SourceExpression calls the OpenCDS method, which returns a new value for the Caption property of the ActionItem. This, in turn, causes the two buttons that are using this action to likewise adopt the caption. It also performs the rather simple side effect of closing or opening the ClientDataSet. However, there are really very few limits to the side effects that could have been implemented.

I am sure that more than a few people will be thinking to themselves that this is just another example of a LiveBinding that does something we can already do (open and close a ClientDataSet). For example, couldn't we have simply used the following OnExecute event handler?
procedure TForm1.Value1EditChange(Sender: TObject);
begin
  if TActionItem(Sender).Caption = 'Open' then  
  begin
    ClientDataSet1.Open;
    ClientDataSet1.LogChanges := False;
    TActionItem(Sender).Caption := 'Close';
  end
  else
  begin
    ClientDataSet1.SaveToFile;
    ClientDataSet1.Close;
    TActionItem(Sender).Caption := 'Open';
  end;
end;

My response to has three parts. First, the second event handler, the one that explicitly opens and closes the ClientDataSet, needs to have intimate knowledge of what operation has to be performed. By comparison, the first event handler simply notifies the expression engine that something about the Sender has changed. The first event handler has no details about the change, nor does it specify what should happen in response. The expression engine does the actual assignment based on the LiveBinding, and the LiveBinding, not code, defines what happens.

Second, not all LiveBindings require that you notify the expression engine. Many of RAD Studio's LiveBindings, including Lists and Links, require no event handlers at all.

Third, for those LiveBindings that do require an event handler, all of them could potentially refer to this one, simple event handler, the one that calls the Notify method of the BindingsList. As a result, a form that uses LiveBindings to perform its various tasks may have zero or just one event handler. By comparison, if you performed those tasks using code, there would have to be many different event handlers, each one invoking its specific task and requiring specific knowledge about the operation they were designed to produce.

Basically what I am getting at is that this usage of a LiveBinding is profoundly different than normal event handler usage. The OnAction event handler is completely agnostic, as far as the operation that will result from its invocation. All of the behavior is defined declaratively in the LiveBinding, along with the actions defined in any methods that are invoked during the evaluation of the SourceExpression. What's even more exciting is that in the future this type of effect might be achieved without any event handlers at all.

But please do not get me wrong. I am not advocating that we should start replacing traditional event handlers with LiveBindings. That would be completely missing the point. LiveBindings have their place, and event handlers have their place as well.

Successful use of LiveBindings requires us to look at our goals from a different perspective. LiveBindings can do things that really don't fit into the event-driven model of traditional event handlers. Sure, in this version of RAD Studio they are somewhat limited, but that will change over time.

I have one final comment. Earlier in this posting I noted that invoking a method that produces side effects from a LiveBinding expression is similar to the power of side effects produced by property accessor methods. Actually, these two techniques are more closely related than you might think. When designing a new class, you might actually implement a side effect in a property accessor method, and a LiveBinding could then produce that side effect as a result of its assignment of data to the associated property.

On the other hand, side effects produced by property accessor methods are often associated with keeping the internals of that component consistent. By comparison, the types of side effects that you can introduce in methods invoked through LiveBindings can have a much more global impact, keeping many elements of a form, or an entire application, synchronized.

The white paper is available from Embarcadero's Web site at http://edn.embarcadero.com/article/42076.

I am also giving a Webinar on LiveBindings in the RAD in Action series on March 14th. You can learn more about this Webinar at http://www.embarcadero.com/rad-in-action/livebindings.

I am also doing a session on LiveBindings during my Delphi Developer Days 2012 tour with Marco Cantù. Learn more at http://DelphiDeveloperDays.com.

9 comments:

  1. What is still bothering me with LiveBindings is that with the ExpressionEngine it is af we are using Delphi as a script interpreter.

    Errors in expressions will not be noticed by the compiler, so there is a lot of manual checking involved. If I rename a control the compiler will not compile any code that uses the old control name, but with LiveBindings I won't notice the error untill the expression is evaluated at runtime. I hope that in a future update the expressions will be checked by the compiler somehow.

    ReplyDelete
    Replies
    1. Birger: Good points. At design time you can evaluate an expression from the expression editor, but it does not always reflect how the expression will be evaluated at runtime. And, expressions are much slower than compiled code, which is why they are not replacements for traditional event handlers.

      Delete
  2. You're basically trying to use LiveBindings as a poor man's scripting engine, that would lack most of the facilities a regular script engine offers (like compilation, static typing, syntax checks, breakpoints, proper error/exception signaling, leaks protection, etc.).

    If you use DWScript for what you propose f.i., you can syntax checks all the expressions at application startup (rather than having them bomb only when executed), and you get expression capability that goes far beyond what LiveBindings can do, with a much higher execution speed, less "behind the scenes" code involved, more flexibility, more support tools, etc.
    And as a bonus you have the choice between accessing through RTTI (little code required, but fragile and subject to memory overwrites/leaks) or through static, strongly-typed, error-checked wrappers.

    ReplyDelete
  3. LiveBindings (because being string based) are changing a Delphi program from being strongly typed and having compiletime safety into something that is hard to maintain and easy to break (refactoring?).

    In .Net that all works ten time better because the xaml code is being compiler checked while the dfm code is not which is the major design flaw here.

    ReplyDelete
  4. Hi, I view LiveBindings in the following way.

    The sides effects produced by LiveBindings are the consequence of its implementations: it uses an Observer Pattern (I think LV is a mix of ModelViewPresenter / Interpreter Patterns; Observer is used inside the ModelViewPresenter). In general an observer can be used to keep application's components synchronized. E.G. the subject "opendatataset" can be subscribed by a form in order to activate the datasource; a datamodule could trigger the subject calling the Notify method of the observer when one of its dataset has been opened.

    BindingsList1.Notify(Sender, '') means. I call the observer manager (BindingList1) to inform that my state (sender) has been changed. The observer manager notify the subscribers.
    The subscriber is a control component in the binding expression.

    Regards,
    Stefano Moratto
    www.csiat.it

    ReplyDelete
  5. I really appreciate the comments that have been posted so far. Embarcadero is aware of the limitations and weaknesses that have been mentioned. The good news is that this was just the first release of LiveBindings. I expect that we will continue to see improvements and enhancements in future releases.

    ReplyDelete
  6. Won't the breakthrough that Live Bindings promises be the ability to connect objects to UI controls so that we can move away from DB aware components to true OOP programming ? In this paradigm, an object can be created and loaded (perhaps by using an ORM framework such as TMS Aurelius) and then linked via LiveBindings to UI controls as well as other objects. This seems to be the preferred way to work in VS for some time already. To the non-techno developer, such as myself, there has always been a gap between implementing OOP principles and interacting with the UI. Will LiveBindings be the solution ?

    ReplyDelete
    Replies
    1. The problem currently is a similar as in .Net (but where you have the ability to do AOP to your rescue) - property change notifications. You have to implement setters for every property you want to bind to (at least when you want to be notified if that property has changed).

      Also it requires the controls to be binding aware which means when you type into an edit for example it should send the updated value directly or after leaving the control (or after validation) to the bound object.

      This all works kinda with LiveBindings currently (although some things still require some or lots of extra code).

      But when it comes to for example bind a list of objects to some list control or grid LiveBindings reveal their weakness. They are just a collection of expressions and all these expressions have to be defined resulting in like a dozen different expressions which are very brittle.

      That is why my approach in DSharp was using data templates as in WPF to specify how to display these items inside a list, tree or grid. Of course that is not RAD anymore but to me a less error prone way is better than the RAD way in that case.

      Delete
  7. "What is still bothering me with LiveBindings is that" is that Embarcadero has a perfect system of Publisher/Subscriber in the Bold source since 10 years. Probably some adaptation must be done to include it in the VCL without the Business object space.
    Some advantages e.g.
    == No events to add with Notify,
    == The possibility to have a property of type TBoldApplyPolicy for each binding :
    (bapChange - Changes in the GUI control are immediately applied.
    bapExit - changes in the GUI control are applied when the control loses focus
    bapDemand - changes in the GUI control is not applied until it is explicitly requested.)
    == The use of OCL: this language is used in UML, have a lot of reasearch on it and is recognized as a standard.
    It is obvious that the current implementation of the Embarcadero binding is not very successfull - look only to the traffic on the livebinding ng).
    In this area, I thing that the proposed implementation of Boian Mitov "Visual Live Binding" is really revolutionary and probably most in the spirit of a RAD like Delphi.
    Stefan Glienke has too an interesting implementation of the binding but as he said him self not visual (at this stage).

    ReplyDelete