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.