Thursday, January 28, 2010

An Object Binding Prototype

I have been really looking for good ways to keep the different layers of our application bound together. As such I have been looking at others code, searching out libraries, and building prototypes.

One such prototype I have built, is fairly simple. While, I am not sold on using this as a method for binding, I think its worth sharing. It requires Delphi 2010, and uses the enhanced
RTTI.

First off the TObjectBinder code can be downloaded from SVN Repository in my RTTIWork Branch.

Say you have class such as this one:
TPerson = class(TObject)
  FirstName : String;
  LastName : String;
end;

To use this code all you need to tie this object to your user interface is this:

  // Create Binding with Class you want as the source of the binding
  Binding := TObjectBinder.Create(TPerson);
  // Binding() Parameters
  // First: Property or Field of Class passed to the Constructor
  // Second: Object you want as the destination of the binding.
  // Third:  Property or Field of the Destination you want to bind too
  // Fourth: If the binding is readonly(Optional: Default False)
  // Fifth: ITypeMapping Interface (Optional: Default NIL) more on this later.
  Binding.Binding('FirstName',Edit1,'Text');
  Binding.Binding('LastName',Edit2,'Text');
  // Then there are two key methods you can use after that.
  // Load() takes the contents of the Person and populates
  //        Edit1 and Edit2 Controls
  Binding.Load(Person); // Where Person is an instance of TPerson
  // This takes the contents of Edit1 and Edit2 and Saves it back to the person.
  Binding.Save(Person);

What does this offer, allows you create a list of bindings of any property of any object
to any other property of any object. It reduces code like this...

  Edit1.Text := Person.FirstName;
  Edit2.Text := Person.LastName;
  ...
  Person.FirstName := Edit1.Text;
  Person.LastName := Edit1.Text;

I have buttons that need to be enabled only when the data changes, so I also implemented
Button1.Enabled := Binding.IsChanged(Person);

Since mappings may need to change data types, I created the following interface
to allow you to specify conversion methods and supply them.

  ITypeMapping = interface
     function SourceToDest(Source : TValue) : TValue;
     function DestToSource(Source : TValue) : TValue;
     function Compare(Source : TValue; Dest : TValue) : Integer;
  end;

Granted, if this was more than a prototype I would have built all of the common conversions
and automatically detected which one to use.

To take this further, I have a need to dynamically create a form that can edit an unknown object. Similar to what you can do with an Object Inspector, so this might work well to bind
to those dynamically create controls. Time to build another prototype :-)

Special thanks to Cobus Kruger for this great post and another option on doing the same thing.  In that solution, I did not like having to name my controls to have to match my model.   But the basic idea is very similar, and shows off yet another way to use the RTTI and potentially solve the same problem.

12 comments:

  1. Great idea, I have been loking for something similar.

    Regards,
    Ajasja

    ReplyDelete
  2. Nice! But how about data validation? When you call Binding.Save(Person), some properties could be saved, but for example last one raises exception (StrToInt or so)...

    ReplyDelete
  3. It's a prototype, validation could be handled many different ways. But a method on the ITypeMapping interface could do it.

    ReplyDelete
  4. Why didn't you use one of the existing frameworks? We have very good experience with tiOPF that does just this and a lot more!

    ReplyDelete
  5. Each potential solution has pros and cons.

    We(My employer) have our model classes already built. tiOPF, Instant Objects, etc… would required change to our model.

    A critical requirement is that our model classes need to cross compile between .NET (Prism) and Win32. RemObjects SDK and DataAbstract where considered as they could have done this but I did not have 15k in my budget to justify buying this for my team.

    Our DB persistence layer is nearing completing and is 100% .NET we communicate via a web service to our application.

    We communicate between .NET and WIN32 using the same class model. Using the XmlSerializer I have posted before on the Win32 side, and the one built into the .NET Framework on our web service side.

    So locally our models are persisted and stored in XML.

    So ultimately we have one item left in our architecture that still needs some vetting. That is binding the user interface to the model. Which still has several optional solution and hence the need to develop prototypes.

    ReplyDelete
  6. looking this:
    Binding.Bindng('FristName', edit1, 'text')
    is there no way to have *intellisense* used for properties? My thought here is, that this could be very error-prone coding.

    ReplyDelete
  7. @Frenk
    I would agree, and as such the next prototype I will be posting works also on the design surface and uses property editors to help with this very problem.

    ReplyDelete
  8. Can you this used with records instead of classes?

    ReplyDelete
  9. Ups I fogot a see

    Can you see this used with records instead of classes

    ReplyDelete
  10. Yes, you could modified, to work with TValue instead of TObject which would then work with both.

    Might not be a bad idea to do it that way, I don't have need to do it.

    ReplyDelete
  11. Hi Robert, I wrote a generic TRecordObjectBinder that works with record (but I had to modify your base class), whould you be interested in merging the code?

    ReplyDelete
  12. Hi Roberto, the property Live Binding in XE2 make the same thing? like bind simple property classes with components?

    ReplyDelete