组件流在运行时和设计时的差异

Differences in component streaming at runtime and design time

我有一些代码在 运行 时可以正常工作,但在 IDE 时不能。我找不到任何有关组件加载方式不同的文档来帮助我。

我开发了一些定义资源的组件。我还开发了一个包含这些资源定义的自定义 TDataModule 后代。因为我希望能够在项目之间共享定义,所以我有一个简单的 TComponent 后代,可以在其中嵌入 TDataModule - 这个想法是在表单上删除适当的 TComponent 或数据模块包含嵌入式 TDataModule.

上的所有资源定义

这似乎在 运行 时按预期工作,但在设计器中却没有。

我会尽量简明扼要地解释操作。涉及很多代码,但希望以下内容足以理解正在发生的事情。

有一个全局 TDictionary 用于注册资源定义。这些定义使用二进制 UUID(一个 16 字节数组)作为键进行注册。资源的 UUID 设置为字符串表示形式(以便于流式传输)。设置字符串值后,setter 方法会检查自身并向上检查它或 Owner TComponent 中的任何一个是否匹配 csLoading in ComponentState,如果有则匹配字符串值暂时保存。如果不是,则资源定义会在全局字典中注册自己。 (这是必要的,因为在 AfterConstruction 中分配了一个随机 UUID,此时 csLoading 标志未设置,因此必须检查父项)。

当资源定义在 Loaded 调用中完全流式传输时,临时字符串用于设置 UUID 并注册资源。

包含 TDataModule 定义的 TComponent 基于具有以下模板代码的根 class:

type
TITIODataResourcesLoader = class(TDataModule)
 ...
end;
TITIODataResourcesLoaderClass = class of TITIODataResourcesLoader;

TITIOResourceInclusion = class(TComponent)
protected
  _pResources:    TITIODataResourcesLoader; 
  class function  _GetModuleClass(): TITIODatResourcesLoaderClass; virtual; abstract;
public
  destructor Destroy; override;
  procedure AfterConstruction(); override;
end;

destructor TITIOResourceInclusion.Destroy;
begin
  FreeAndNil(Self._pResources);
  inherited;
end;

procedure TITIOResourceInclusio.AfterConstruction;
begin
  Self._pResources:=Self._GetModuleClass().Create(Self);
  inherited;
end;

我有许多资源的组件编辑器,它允许通过枚举 class 资源的全局 TDictionary 中的值来选择资源。因此,例如,一种资源定义类型是数据库 table 的定义,另一种资源定义类型是数据库 table 连接的定义。在用于定义加入的组件编辑器中,用户可以通过选择在全局 TDictionary.[=35= 中注册的任何 table 来选择要加入的 table ]

在一个测试项目中,它有一个 TITIODataResourcesLoader 定义了一些 table 类型,还有一个 TITIOResourceInclusion 包括另一个 TITIODataResourcesLoader(在不同的BPL 文件)然后在 运行 时,连接编辑器(这是一个 VCL 表单)将显示所有 table,但在设计器中仅显示定义在测试项目的 TITIODataResourcesLoader 可用。如果我打开 BPL 文件中包含的 TITIODataResourcesLoader,则在测试项目的 TITIODataResourcesLoader.

的设计器中注册并选择 table 上的资源

所以看来 运行 时 TITIOResourceInclusion 的流式传输不会导致包含的 TITIODataResourcesLoader 中的组件被注册, 但在 运行它按预期工作的时间

所以我的问题和困惑是:Designer 中的组件流有何不同?

我问“Designer 中的组件流有何不同?”

我在 TDataModule 派生的 class 中添加了对方法入口点的 Application.MessageBox 调用,我可以确认在 运行 时我们看到以下内容:

TOuterDM.Create
  TInnerDM.Create
  TInnerDM.Loaded
  TInnerDM.AfterConstruction
TOuterDM.Loaded
TOuterDM.AfterConstruction

这是具有 DFM 文件的组件的正常订单。

但是在设计器中我们得到:

TOuterDM.Create
TOuterDM.AfterConstruction
  TInnerDM.Create
  TInnerDM.AfterConstruction
TOuterDM.Loaded

这表明 Designer 没有调用 Create 而是为外部和内部数据模块调用 CreateNew,然后流式传输外部数据模块而不是内部数据模块。我没有检查源代码,但我认为这是因为设计者只为 'root instance'

流式传输

通过将这段代码添加到我的 TDataModule 派生组件中,我已经设法获得了我想要的行为:

  procedure TITIODataResourcesLoader.AfterConstruction;
  begin
    // Original code for the deferred load
    if(not(csDesigning in Self.ComponentState)) then
    begin
    // Because this is a component with a DFM file, AfterConstruction is called AFTER
    // the streaming is complete
      Self._LoadDeferredResources();
    // Any 'OnCreate' handler is called AFTER we have initialised ...
    end
    else
    begin
      // this code added to stream in the designer
      if(Self.Owner is TITIOResourceInclusion ) then
        InitInheritedComponent(Self, TDataModule);
    end;
    inherited;
  end;