如何让 Delphi 序列化动态创建的控件以及附加到它们的事件?

How to get Delphi to serialize dynamically created controls along with events attached to them?

我有一个容器控件 TFramedScrollBox。这又包含 TFlowLayout,后者又包含 TPanelTRectangle

同样 TPanel 可以包含其他控件,如编辑框、组合框等。

所以层次结构是这样的:

TFramedScrollBox
   |->TFlowLayout 
         |-> TPanel 
         |     |-> EditBox
         |     |-> ComboBox
         |     |-> DateTime Picker
         |     |-> Label
         |     |-> CheckBox
         |     |-> Radio Buttons
         |     |-> ListBox
         |-> TRectangle 

实际上TFramedScrollBox中的所有控件(TRectangle除外)都是在运行时动态创建的。在创建时,我根据不同的标准在必要时将事件附加到每个控件。

这是我用来将事件附加到控件的代码是创建时间:

pnlNewRow.OnClick := RowClick;
pnlNewRow.OnMouseEnter := RowMouseEnter;
pnlNewRow.OnMouseLeave := RowMouseLeave;
pnlNewRow.OnDblClick   := RowDblClick;

我想将容器控件中的所有控件及其子组件和子子组件以及附加到 SQLite 字段中每个控件的事件一起保存。并在未来从 SQLite 加载所有组件并在表单上创建它们。

我尝试序列化容器组件,但只有组件被序列化,附加到它的事件没有被序列化。

这是我用来序列化组件的代码片段:

procedure TfrmMain.Button1Click(Sender: TObject);
var
  MS: TMemoryStream;
  TS: TStringStream;
begin
  MS := TMemoryStream.Create;
  MS.WriteComponent(sbMain);
  MS.Position := 0;
  TS := TStringStream.Create;
  ObjectBinaryToText(MS, TS);
  TS.Position := 0;

  ShowMessage(TS.DataString);
end;

请指导我如何保存组件链接到的所有事件以及附加到它的自定义属性(我计划在将来添加)。

要序列化您需要手动编写代码的事件。我看不出任何其他方式。

请编译并运行这个最小的 FMX 项目

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
  protected
  public
  end;
[...]
function ComponentToString(AComponent : TComponent) : String;
var
  SS : TStringStream;
  MS : TMemoryStream;
  Writer : TWriter;
begin
  SS := TStringStream.Create('');
  MS := TMemoryStream.Create;
  Writer := TWriter.Create(MS, 4096);

  try
    Writer.Root := AComponent;
    Writer.WriteSignature;
    Writer.WriteComponent(AComponent);
    Writer.FlushBuffer;
    MS.Position := 0;
    ObjectBinaryToText(MS, SS);
    Result := SS.DataString;
  finally
    Writer.Free;
    SS.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Text := ComponentToString(Form1);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  //
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  //
end;

end.

单击 Button1 将其写入 Memo1:

object Form1: TForm1
  Left = 224
  [...]
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  DesignerMasterStyle = 0
  [...]
  object Button1: TButton
    IsPressed = True
    [...]
    OnClick = Button1Click
  end
  object Memo1: TMemo
    [...]
  end
end

如您所见,其中包括 Form1 的两个事件处理程序和一个事件处理程序 Button1 的内容,类似于 .DFM 将包含的内容。这不是你想要的吗?

并且由于您可以将 ComponentToString 存储为库例程,所以我不确定说“要序列化您需要手动编写代码的事件”是否正确,好吧,无论如何都不是特定于表单的代码.

顺便说一句,这适用于动态创建的控件。在表单的 Private、Protected 或 Public 部分添加一个名为 Button 的 TButton,并将以下代码添加到 FormCreate 处理程序

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button :=  TButton.Create(Self);
  Button.Name := 'Button';
  Button.Parent := Self;
  Button.OnClick := Button1Click;
end;

单击任一按钮会生成与上述列表类似的列表,但会添加额外的动态创建按钮。