Eric G
Eric G

Reputation: 3517

Using TPrototypeBindSource to automatically update properties in an object

I'm trying to use livebindings to update properties of my (non-component) objects. I have a TPrototypeBindSource that I am using to bind components to my objects fields, and using a TObjectBindSourceAdapter at run time. I can get it to work if call MyPrototypeBindSource.Refresh in the onchange event of the edit components, but is there a way to get this to happen automatically, without setting up the onchange event for every component on my form?

Upvotes: 1

Views: 2372

Answers (1)

Sir Rufo
Sir Rufo

Reputation: 19106

Although there is TPrototypeBindSource.AutoPost which I suspect to handle the automatic post of the control data to the data object it does not ... well looking at the source, this property just influence the internal data generator.

Seems to be, we have to set this property by hand when creating the adapter (and because we are just at this point we will set the AutoEdit as well):

procedure TForm1.PrototypeBindSource1CreateAdapter( Sender: TObject; var ABindSourceAdapter: TBindSourceAdapter );
begin
  FPerson := TPerson.Create;
  ABindSourceAdapter := TObjectBindSourceAdapter<TPerson>.Create( Self, FPerson );
  ABindSourceAdapter.AutoEdit := True;
  ABindSourceAdapter.AutoPost := True;
end;

This will do the job, every time you leave an TEdit but a TCheckBox will post the data immediately.

To change this just use a published method

procedure TForm1.ControlChanged( Sender: TObject );
begin
  if Sender is TComponent
  then
    TLinkObservers.ControlChanged( Sender as TComponent );
end;

and assign this to each needed control (e.g. TEdit.OnChange) to get the data immediately to the data object.

Here the whole in one go

type
  TPerson = class
  private
    FFirstname: string;
    FLastname: string;
    FActive: Boolean;
  public
    function ToString: string; override;

    property Active: Boolean read FActive write FActive;
    property Firstname: string read FFirstname write FFirstname;
    property Lastname: string read FLastname write FLastname;
  end;

  TForm1 = class( TForm )
    PersonSource: TPrototypeBindSource; { OnCreateAdapter -> PersonSourceCreateAdapter }
    Edit1: TEdit; { OnChange -> ControlChanged }
    Edit2: TEdit; { OnChange -> ControlChanged }
    BindingsList1: TBindingsList;
    LinkControlToField1: TLinkControlToField;
    LinkControlToField2: TLinkControlToField;
    Label1: TLabel;
    ApplicationEvents1: TApplicationEvents; { OnIdle -> ApplicationEvents1Idle }
    CheckBox1: TCheckBox;
    LinkControlToField3: TLinkControlToField;
    procedure PersonSourceCreateAdapter( Sender: TObject; var ABindSourceAdapter: TBindSourceAdapter );
    procedure ApplicationEvents1Idle( Sender: TObject; var Done: Boolean );
  private
    FPerson: TPerson;
  published
    procedure ControlChanged( Sender: TObject );
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ApplicationEvents1Idle( Sender: TObject; var Done: Boolean );
begin
  // just for checking then object data
  Label1.Caption := FPerson.ToString;
end;

procedure TForm1.ControlChanged( Sender: TObject );
begin
  if Sender is TComponent
  then
    TLinkObservers.ControlChanged( Sender as TComponent );
end;

procedure TForm1.PersonSourceCreateAdapter( Sender: TObject; var ABindSourceAdapter: TBindSourceAdapter );
begin
  FPerson := TPerson.Create;
  ABindSourceAdapter := TObjectBindSourceAdapter<TPerson>.Create( Self, FPerson );
  ABindSourceAdapter.AutoEdit := True;
  ABindSourceAdapter.AutoPost := True;
end;

{ TPerson }

function TPerson.ToString: string;
begin
  Result := FLastname + ', ' + FFirstname + ' ' + BoolToStr( FActive );
end;

LiveBindings:

Active    : ftBoolean -> CheckBox1/CheckedState(Self)
Firstname : ftString  -> Edit1/Text
Lastname  : ftString  -> Edit2/Text

If you do not like to assign the ControlChanged method to all controls you can force the TPrototypeBindSource to post the data by calling TPrototypeBindSource.Post. But you have to check first if it is in edit mode:

if PersonSource.Editing
then
  PersonSource.Post;

Call this whenever you need to have the data posted ... if at any time just call it within TApplicationEvents.OnIdle.

Upvotes: 4

Related Questions