Reputation: 3517
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
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