组件流在运行时和设计时的差异
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;
我有一些代码在 运行 时可以正常工作,但在 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
.
所以看来 运行 时 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;