FluentLiveBindings and MVVM

Reposed from the list (with no replies) just before the forum launch.

This question could easily be just for @Malcolm, but figure others might have some knowledge…

I’m exploring the Fluent LiveBindings offering that Malcolm introduced a while ago.
[GitHub - malcolmgroves/FluentLiveBindings: Simple library to make creating LiveBindings in code much easier.]

The examples are focused on binding a UI TComponent to either another TComponent or TBindSourceDB.

I’m wanting to bind a UI component to a vanilla TObject instance that serves as a viewmodel to the form.

Example:

  TViewModel = class
  private
    ...
  public
    CompanyName : string  read ... write ...;
  end;

So a form with an edit field might be bound with:

  BindingsList1.BindComponent(CompanyEdit)
               .ToComponent(fViewModel, 'CompanyName')
               .BiDirectional;

That won’t compile unless I change my vm to…

    TViewModel = class(TComponent)

…which works, but without two way support:

For example:

    BindingsList1.BindComponent(CompanyEdit)
               .ToComponent(fViewModel, 'CompanyName');

As soon as I add .BiDirectional, it raises an exception:

Project SimpleCodeBindings.exe raised exception class EEvaluatorError with message 'Expected an identifier, number or string.

Unless I’m mistaken, I’m lead to believe that LiveBindings does work with plain objects. Am I missing the obvious? Is that something that could be added to the framework?

ps I don’t want to be confined to using TComponent descendants as my non-visual view model even if the BiDirectional bit worked.

Any suggestions appreciated.

Hi Ian,

I’m running from memory, is there not a .ToObject method there instead of .ToComponent. I know I have done this, but when I’m back in front of my machine I’ll have a look.

Cheers

Yes you’ve been led to believe that but in actuality it’s not really the case. At least not in the way you’re imagining it should work.

There is code to be written and hoops to be jumped through to use Livebindings with Plain Old Delphi Objects (PODOs). I don’t remember to what extent Malcolm’s library encapsulated those steps.

You might also find this series of articles interesting.

1 Like

Thanks Malcolm. There’s no ToObject in the code that I can see. Would be great if you’ve got working code.

Thanks Lachlan. I’ll have a read of that post. There’s an old YT video of David I stating this possibility, then goes on to give an example using TForm as the object!
I’m somewhat miffed that this isn’t a standard feature of LB.

It is a standard feature of LiveBindings, since the beginning. This post from 2012 has more details http://www.malcolmgroves.com/blog/?p=1084

However, I can’t recall how I did it in my library, but Ill check when I’m back.

Malcolm’s post is probably a better one to start with than the one I linked to.

I am a fan of LiveBindings when used with DataSets but I’m not a fan of how you bind to TObjects. The amount of work seems disproportionate compared to writing the code to manually fill the TObject values to/from the UI controls.

Maybe I should go back and have another look at the FluentLiveBindings and see if I’m missing something there in regards to PODO support.

I now remember trying out MenialTasks (eg. on different fmx platforms) way back when. Jumping through hoops with prototype-this and adaptor-that made me dizzy. This stuff is trivial in other languages. I’ll gladly buy @Malcolm a beer if ToObject() works. :wink:

As an aside, I’ve used the Grijjy Starter Kit with success. Stefan’s DSharp is another option.

OK, now I’m motivated :slight_smile:

I’m remembering more. I recall I had overloaded versions of ToObject. One where you could pass in a TAdapterBindSource if you had one on your form already, and another which would create it all for you.

If that’s not in there, it means I have a later version that I’m using that I haven’t pushed (or I’m going insane, which with kids schooling from home, can’t be ruled out)

1 Like

TAdapterBindSource … a descendant of TBindSourceAdapter

aaargh. :slight_smile:

If only we had a TAdapterBindSourceFactory in there

OK, thought so. I just pushed the local version I’ve been using. It has support for ToObject in both component and list bindings. I haven’t done grids yet, as I haven’t needed them but can if enough beers are offered.

Also, the bit I remembered about it creating the bindsourceadapters for you was not true. I found that as a ToDo in my notes, so I was mis-remembering that part.

Just a reminder, the point of this library isn’t to replace the out of the box LiveBindings. If you don’t like LiveBindings, this probably won’t change that. All this does is let you partially or completely replace the usage of the LiveBindings designer. It still expects whatever BindSources, BindingLists, etc that LiveBindings would, and once the bindings are done, it will behave the same way*. It just let’s you create the bindings in code more easily.

(*) That’s almost true. There are a couple of things that LiveBindings supports, but that the Designer does not. So this will let you have a two way binding between two checkboxes, for example, whereas the last time I tried that in the designer it wouldn’t allow it. Don’t know why you’d want that, but you can :slight_smile:

1 Like

Awesome, thanks Malcolm.
OK, so ToObject() only accepts a TAdapterBindSource for now. I can live with that. Hiding adapters would have upgraded you from a schooner to a pint, which may still be the case if everything goes to plan.
Re your reminder - I only dislike LBs because of all the visible scaffolding. Simplifying this to a single line to wire the view to the viewmodel changes that.
ps Melbournians can help you with lockdown insanity. We’re experts.
Cheers

I’m experimenting with the code example SimpleCodeBindings.dpr in Malcolm’s just updated FluentLiveBindings.

It shows binding of a TEdit to a TFoo (TObject descendant) with changes to the TEdit reflected back in the TFoo. That’s working fine.

What I’m trying to do now is get changes made to the TFoo in code to be updated to the TEdit after the binding is activated.

I changed the binding to be bidirectional

procedure TForm4.Button10Click(Sender: TObject);
begin
  BindingsList1.BindComponent(Edit12)
               .ToObject(AdapterBindSource1, 'Firstname').BiDirectional;
end;

and I changed the implementation of TFoo to

  TFoo = class(TObject)
  private
    FFirstname : string;
    procedure SetFirstName(const Value: string);
  public
    property Firstname : string read FFirstName write SetFirstName;
    constructor Create(const Firstname : string); virtual;
  end;

implementation

procedure TFoo.SetFirstName(const Value: string);
begin
  FFirstName := Value;
  { from memory we now need something like the following for the 
  LiveBindings framework to know the value was changed by other code }
  Form4.BindingsList1.Notify(Form4.AdapterBindSource1, 'Firstname');
end;

I then added a new button which just sets the value of FFoo.Firstname to something to other than ‘Malcolm’. If I’ve already activated the binding to the TEdit though my changes via other code to the FFoo.Firstname property are propagated to the TEdit.

I’m don’t think I’m right with that last line of the TFoo.SetFirstname setter. Anyone got any suggestions for how to correct it?

I always think of BindingsList.Notify as being for alerting the bindings system to changes in controls that aren’t automatically picked up by the observer.

What you are describing is coming the other way, a change in your source data that you want reflected in your controls. For that I use AdapterBindSource.Refresh.

In my simple brain, I think of bindsources as being like datasets. Just as a dataset exposes the underlying db, the adapterbindsource exposes your underlying objects. So a change in the adapterbindsource will need to be posted to the objects, and equally the adapterbindsource will need to be refreshed to pick up changes in the underlying objects.

If I think of it that way, a lot of things are easier.

Cheers
Malcolm

A whole pint! Alright, hold my coat :slight_smile:

In all seriousness, all the way through this I had a constant debate with myself about where the boundaries of this lay. How much of Livebindings should I hide vs how much should I expect from the user? I kept coming back to the original point: allow me to do what the designer does, more easily in code. Bindsources are a core concept in LiveBindings, so hiding them seemed like overstepping that boundary.

However, for a pint, I might throw my scruples out the window.

I think what I actually want is a smarter BindSource, one where I don’t have to muck about with Adapters. That might actually be the better solution.

OK that seems to be working. The TFoo.Firstname property setting is now cause the TEdit value to change when FFoo.Firstname is changed in code.

procedure TFoo.SetFirstName(const Value: string);
begin
  FFirstName := Value;
  Form4.AdapterBindSource1.Refresh;
end;
1 Like

Now you’re talking multiple pints :slight_smile:

     Form4.AdapterBindSource1.Refresh;

I can see this working with the AdapterBindSource injected to a viewmodel for the Foo UI (to hide the form implementation).
Thanks @Malcolm and @Lachlan for your collective input.

You mean you don’t normally maintain a reference to your form from within your business objects? :laughing: