在表单中实时绑定现存的用户对象

livebinding an extant user object in a form

我正在尝试在 VCL 窗体上使用实时绑定,其中要绑定到窗体控件的对象作为 属性 传递给窗体。我正在使用 10.1 柏林。对象中传递的属性是普通的:

 Public
      Property ProjectObject: TProject Read fProjectObject Write fProjectObject;

我已经使用 DataGeneratiorAdapter 和 AdapterBindSource 来使用设计器在表单上设置链接。

我不理解的地方是 OnCreateAdapter 方法中的 AdapterBindSource。我能找到的所有示例都显示了如何创建一个由控件填充的新对象,但我找不到在运行时绑定 fProjectObject(传递的对象)的方法。

我当前在 OnCreateAdapter 方法中的代码是:

ABindSourceAdapter := TObjectBindSourceAdapter<TProject>.Create(Self);

编译器可以接受,但不允许控件显示和更新 fProjectObject 中的属性。

显示此表单(项目编辑表单)的代码部分如下所示:

ProjEdit.ProjectObject := Proj;
ProjEdit.ShowModal;
StoreProject(Proj);

其中ProjEdit是项目编辑表单,ProjectObject是传递项目对象的属性,Proj是要编辑的项目对象。项目对象简单地传递给这个表单,并在对信息进行任何更改后存储。此对象在传递给此表单进行编辑之前存储在数据库中。

如何将实时绑定连接到传递的对象?

在此先感谢您的帮助

我认为可能让您感到困惑的是您的 ProjectObject 需要的不是很明显的一点 在 事件触发 CreateAdapter 之前创建。以确保 发生这种情况,您需要重写表单的 Create 方法并在那里创建您的 ProjectObject。

以下对我来说很好用:

type

  TPerson = class
  private
    FLastName: String;
    FFirstName: String;
  public
    property FirstName : String read FFirstName write FFirstName;
    property LastName : String read FLastName write FLastName;
  end;

  TForm1 = class(TForm)
    edFieldA: TEdit;
    edFieldB: TEdit;
    BindNavigator1: TBindNavigator;
    PrototypeBindSource1: TPrototypeBindSource;
    BindingsList1: TBindingsList;
    LinkControlToField1: TLinkControlToField;
    LinkControlToField2: TLinkControlToField;
    procedure PrototypeBindSource1CreateAdapter(Sender: TObject; var
        ABindSourceAdapter: TBindSourceAdapter);
  private
  public
    Person : TPerson;
    constructor Create(AOwner : TComponent);  override;
  end;

[...]

constructor TForm1.Create(AOwner: TComponent);
begin
  Person := TPerson.Create;
  Person.FirstName := 'John';
  Person.LastName := 'Smith';
  inherited;
end;

procedure TForm1.PrototypeBindSource1CreateAdapter(Sender: TObject; var
    ABindSourceAdapter: TBindSourceAdapter);
begin
  ABindSourceAdapter := TObjectBindSourceAdapter<TPerson>.Create(Self, Person, False);
end;

更新 不必在表单上创建表单上的 Person 对象。它可以简单地分配给以前存在的对象,如

constructor TForm1.Create(AOwner: TComponent);
begin
  Person := SomeTPersonObjectCreatedAlreadyInOtherCode;
  inherited;
end;

如果您想在我的示例中验证这一点,请在单元的初始化部分创建一个 TPerson 实例,并在表单的 Create 构造函数中将 Form1.Person 分配给它。您可能没有意识到的是 Delphi 对象变量实际上是一个指针,因此它可以自由地 "pointed at" 对象的现有实例。

重要的是把TObjectBindSourceAdapter的最后一个参数设置为False,这样adapter就不会拥有Person对象,否则它(adapter)销毁的时候也会销毁Person对象。

顺便说一句,此视频中解释了重写表单构造函数的必要性:

https://delphiaball.co.uk/2015/10/19/livebindings-in-vcl-part-2-livebinding-objects/

他解释说,如果您没有在 CreateAdapter 事件之前创建要绑定的对象,绑定将清除该对象在绑定字段中已有的任何内容。

这是我的建议:

首先:在 AdapterBindSource 的 CreateAdapter 中使用以下内容:

procedure TfrmProjectEdit.AdapterBindSource1CreateAdapter(Sender: TObject; var ABindSourceAdapter: TBindSourceAdapter);
begin
  fProjectObject:=TProject.Create;
  ABindSourceAdapter:=TObjectBindSourceAdapter<TProject>.Create(self, fProjectObject, True);
end;

第二种:对项目属性使用setter例如:

procedure TfrmProjectEdit.SetProject (aProject: TProject);
begin
  fProjectObject:=aProject;
  AdapterBindSource1.Refresh;
end;

快速解释:AdapterBindSource 将拥有 fProjectObject 并在释放 ABS 时释放它。我们简单地为 fProjectObject 分配一个新值并刷新 setter 中的 ABS。

我还没有测试过这段代码 - 但我认为这应该可行...