问题
In Delphi XE2 LiveBindings, I need to bind a VCL control of any type to a property of any type on an arbitrary (non-component) object. I can do this unidirectionally. But I need to do it bidirectionally.
Let's say I want to bind a TPerson.PersonName: string to a TEdit.Text.
What I have now is simple.
- Create a new VCL application, add a TBindScope, TBindingsList, TEdit.
- Create an instance of TPerson named person1.
- Using a BindingList, add a TBindExpression property.
- With BindExpression
- set ControlComponent to Edit1
- set ControlExpression to 'Text'
- set SourceComponent to BindScope1
- set SourceExpression to PersonName
- Add a button; to the Click event I add: BindScope1.DataObject := person1;
- Add a button; to the Click event I add (only one is necessary, but until it works I will try them both).
- TBindings.Notify(sender, '');
- BindingsList1.Notify(sender, '');
The first button binds in the first direction. The second button never seems to write the value back to the person1.PersonName property.
I've experimented with the notification code, the binding direction, binding types, expressions, SourceMember, etc. Sometimes I get runtime errors in the bindexpression configuration, the rest of the time the binding is simply unidirectional.
I expect to click the second button and see the contents of Edit1.Text written to person1.PersonName.
If I have to do this all from code, I'll consider it and such examples are welcome, but I really want to do it through the designer if possible.
Note that I am not interested in binding between two controls.
Note too that I have already downloaded and inspected the LiveBinding sample projects, and didn't find any that do this. If this is wrong, please be specific when pointing it out. I have also read the DocWiki. It does not cover bidirectional binding except using the DB LiveBinding controls. I am not using the DB LiveBinding controls nor am I using a DataSet. So unless you can explain to me why I should use them, I won't be needing any information about those controls.
回答1:
I've got this working now. I did it all in the designer, but have converted it to mostly code to share it better on SO.
Create a VCL forms project. On the form, drop each of these on the form:
TBindScope TBindingsList TButton TButton TEdit
Rename one of the buttons to btnLoad and the other to btnSave.
Paste this code over your form unit (assuming it is named Form1). Assign the click handlers for the buttons and run it. Click btnLoad to populate the edit box with TPerson object data, edit the text in the edit box to a new value then click btnSave to write it back to the TPerson object.
unit Form1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Rtti,
// LiveBinding units
System.Bindings.Helper, // Contains TBindings class
Data.Bind.EngExt,
Vcl.Bind.DBEngExt,
Data.Bind.Components,
System.Bindings.Outputs;
type
TPerson = class(TObject)
protected
fName: string;
fAge: integer;
procedure SetName(const Value: string);
public
property Name: string read fName write SetName;
property Age: integer read fAge write fAge;
end;
type
TForm1 = class(TForm)
btnLoad: TButton;
btnSave: TButton;
BindScope1: TBindScope;
BindingsList1: TBindingsList;
Edit1: TEdit;
procedure btnLoadClick(Sender: TObject);
procedure btnSaveClick(Sender: TObject);
private
fInitialized: boolean;
fPerson: TPerson;
procedure Initialize;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.AfterConstruction;
begin
inherited;
Initialize;
end;
procedure TForm1.BeforeDestruction;
begin
fPerson.Free;
inherited;
end;
procedure TForm1.btnLoadClick(Sender: TObject);
begin
fPerson.Name := 'Doogie Howser';
fPerson.Age := 15;
BindScope1.DataObject := fPerson;
end;
procedure TForm1.btnSaveClick(Sender: TObject);
begin
TBindings.Notify(Edit1, '');
// Could also do this:
//BindingsList1.Notify(Edit1, '');
end;
procedure TForm1.Initialize;
var
expression: TBindExpression;
begin
// Create a binding expression.
expression := TBindExpression.Create(self);
expression.ControlComponent := Edit1;
expression.ControlExpression := 'Text'; // The Text property of Edit1 ...
expression.SourceComponent := BindScope1;
expression.SourceExpression := 'Name'; // ... is bound to the Name property of fPerson
expression.Direction := TExpressionDirection.dirBidirectional;
// Add the expression to the bindings list.
expression.BindingsList := BindingsList1;
// Create a Person object.
fPerson := TPerson.Create;
end;
{ TPerson }
procedure TPerson.SetName(const Value: string);
begin
fName := Value;
ShowMessage('Name changed to "'+ Value +'"');
end;
end.
来源:https://stackoverflow.com/questions/7478785/need-bidirectional-livebindings-between-a-control-and-an-object