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语句

Delphi XE2 数据模块只需要数据库组件?

在自定义 Delphi 组件中实现 Columns.Columns 属性

如何在 Delphi 中拥有一个在运行时和设计时工作的单例

怎么设置禁止delphi Xe2 自动检查更新

让Delphi XE2程序支持UAC