Delphi XE2 运行时不考虑组件属性
Posted
技术标签:
【中文标题】Delphi XE2 运行时不考虑组件属性【英文标题】:Component property not considered on runtime in Delphi XE2 【发布时间】:2013-07-20 06:02:04 【问题描述】:我正在开发一个组件,但我不能让它考虑在设计时设置的属性。
以下是组件的摘录:
TRVEditFrame = class(TFrame)
...
private
Private declarations
FRVEditor:TCustomRichView;
public
Public declarations
constructor Create(AOwner: TComponent); override;
protected
function GetRVEditor:TCustomRichView;
procedure SetRVEditor(Editor:TCustomRichView);
published
property RVEditor:TCustomRichView read GetRVEditor write SetRVEditor;
end;
...
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetRVEditor(FRVEditor);
...
end;
function TRVEditFrame.GetRVEditor:TCustomRichView;
begin
Result:=FRVEditor;
end;
procedure TRVEditFrame.SetRVEditor(Editor:TCustomRichView);
begin
if Assigned(Editor) then begin
FRVEditor:=Editor;
end;
end;
我可以注册组件,将其放置在表单中并在设计时设置 FRVEditor。
问题是当我运行应用程序时,SetRVEditor() 中的代码没有执行,因为 Editor=nil。
如果我能够在设计时设置 FRVEditor,为什么它在运行时为=nil?我该如何解决这个问题?
由于解释太长,我在此处添加我的其他 cmets
@Kenneth,谢谢你的回复
TCustomRichView 是第三方组件集的一部分,用于管理 超文本文档,并有 4 个更专业的后代,而你 是的,TCustomRichView 不应该在实际应用中使用。 TRVEditFrame 是我正在开发的组件。我的组件背后的想法是创建一个带有菜单、快捷方式、弹出菜单等的单个框架(因此选择组件 TFrame)来管理 4 个 TCustomRichView 后代中的每一个。
这正是我使用 TCustomRichView 的原因:我可以将 4 个后代中的任何一个“插入”到我的组件框架中。这与 TDatasource 的原理相同,可以与 TTAble 和 TQuery 连接(它们具有相同的祖先)。
我想 VCL 没有将 RVEditor 链接到我在设计时设置的 TCustomRichView 后代的原因是因为 TFrame 没有 OnCreate 事件,例如 TForm 。
到目前为止,我设法通过在托管 TRVEditFrame 的 TForm.OnCreate 中手动调用 TRVEditFrame.SetRVEditor 解决了这个问题,但我想知道是否有更好的方法可以做到这一点,这就是我在这里寻求建议的原因。
我知道您也可以为 TFrame 创建 OnCreate 事件,也许我可以将 TRVEditFrame.SetRVEditor 放在那里,但我想知道是否有更好的方法。
关于您评论的最后一部分,我知道注册程序,但考虑到该组件正在开发中。当我开发组件时,我从不将它们安装在 IDE 中,因为我更喜欢将测试内容保留在“官方”之外。 我使用这种方法,一旦组件准备好,我就会使用您提到的程序注册它。如果我想为同一个组件实现其他功能,我可以在测试功能上工作,同时继续使用我在 IDE 中拥有的“官方”功能。
【问题讨论】:
什么是 TCustomRichView?它看起来像一个类。所以属性是一个参考。实际对象在哪里?它存在于表格中吗?对象在用作属性分配源之前是否已经实例化?此外,构造函数中的 SetRVEditor(FRVEditor) 毫无意义。摆脱它。 @David TcustomerRichView 是一个类,并且该属性是对要链接的另一个类的引用 是的,该对象在设计时以与 TRVEditFrame 相同的形式删除。 “对象在用作属性分配源之前是否已实例化?”我不确定你在这里的意思。 TRVEditFrame.RVEditor 在设计时指定。我回答你的问题了吗 @pio pio:当您查看 DFM (alt+F12) 时,您是否看到属性设置为您分配的值? @AlexSC 是的,属性 RVEditor 设置为正确的对象 @pio pio:您能告诉我们您使用的是哪个 Delphi 版本吗?您是否在此框架中使用视觉表单继承? 【参考方案1】:我的建议是更改设计,以便使用 Notification 方法获取引用自定义编辑器的框架组件,而不是手动设置属性。因此,将您的框架类更改为
TRVEditFrame = class(TFrame)
...
private
Private declarations
FRVEditor: TCustomRichView;
protected
procedure Notification(aComponent: TComponent; aOperation: TOperation); override;
public
Public declarations
constructor Create(AOwner: TComponent); override;
public
property RVEditor:TCustomRichView read FRVEditor;
end;
Notification 方法的实现具有将框架连接/断开与自定义编辑器的魔力
procedure TRVEditFrame.Notification(aComponent: TComponent;
aOperation: TOperation);
begin
inherited;
if aComponent is TCustomRichView then
if aOperation=opRemove then begin
if aComponent=FRVEditor then
FRVEditor := nil;
end else
FRVEditor := TCustomRichView(aComponent);
end;
我不知道当编辑器设置/重置到框架时是否需要任何特殊处理,所以这段代码没有做任何特别的事情,只是在适当的时候将组件或 nil 分配给 FRVEditor 数据成员。
【讨论】:
这就是我要找的东西!【参考方案2】:您应该创建子组件。
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited; // Create(AOwner);
FRVEditor := TRichView.Create(self);
end;
DFM 流引擎可能会加载已创建类的属性,但它无法为您创建类,原因有两个:
1) DFM 无法知道对创建的组件进行的任何特殊调整,例如构造函数参数应该是什么(如果有的话)、使用什么构造函数(很多)、附加哪些通知和事件处理程序等等。
2) DFM 无法知道属性应该是哪种类型。例如,我发布了TStrings
oroperty - 应该创建哪个类的对象来感受? TStrings
? TStringList
? TRichEditStringList
? THashedStringList
?它们都继承自TStrings
,因此它们中的任何一个都是 DFM 的好选择 - 但不适用于您编写的组件。
因此,DFM 流子系统负责保存和加载对象的属性,但创建这些对象完全是您的责任。
我相信您也可以通过学习 IDE Designer 如何创建表单并将RVEdit
设为变量而不是属性来偷工减料:
TRVEditFrame = class(TFrame)
...
public
Public declarations
constructor Create(AOwner: TComponent); override;
published
var RVEditor:TCustomRichView; // just like users' forms are created by IDE
end;
然后希望TFrame
构造函数会为您创建此变量的内容。但是这种设计是脆弱的,因为任何外部代码都可能因任何愚蠢的错误而产生类似MyEditFrame.RVEditor := ...
的内容,并导致内存泄漏和编辑器和框架之间所有附加连接的意外故障。
【讨论】:
TRVEditFrame 的目的是创建一个可以“托管” 4 个 TCustomRichView 后代之一的组件,正如我在上面的注释中解释的那样。如果我在构造函数中分配 FRVEditor := TRichView.Create(self) 那么 TRVEditFrame 将只能与 TRichView 一起使用。 然后让它根据需要创建任何后代。如果您愿意 - 制作一个额外的已发布属性以保存所选后代的.ClassName
。但出于上述原因,DFM 流不会为属性创建类。 TReader
不知道应该有哪个类,所以应该是你创建正确的类对象
所以你说我必须在 TForm.OnCreate 中调用 TRVEditFrame.SetRVEditor。我说的对吗?
猜想为时已晚 - 因为 Delphi 4 它 AFAIR 将在 DFM 流式传输已经完成之后。但是你可以拦截它和TForm.Loaded
,看看哪个叫第一。 Loaded
在 DFM 解析后立即被调用,所以你会看到。但我认为你宁愿在TForm.AfterConstruction
中这样做。 docwiki.embarcadero.com/Libraries/XE2/en/…
但是我概述了另一种方法,即认为更好。我重复一遍:在TRVEditFrame
中,您额外发布了AnsiString
属性以保存所选后代的.ClassName
。在运行时,您注册 RVEditors 的所有实现 - RegisterClasses
- 当读取该 AnsiString 属性时,您会按名称找到类并创建其实例。并确保classname property
将在RVEditor
属性之前读取,并且只能在FRVEditor
仍为nil
时更改,并且在编辑器创建后无法更改以上是关于Delphi XE2 运行时不考虑组件属性的主要内容,如果未能解决你的问题,请参考以下文章
delphi中,怎样设置新窗体打开时,就运行指定的SQL语句