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.