0

我正在开发一个组件,但我不能让它在设计时考虑一个属性集。

以下是该组件的摘录:

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?我怎样才能解决这个问题 ?


我在这里添加我的进一步评论,因为解释太长了

@Kenneth,谢谢你的回复

  • TCustomRichView 是管理超文本文档的第三方组件集的一部分,并且有 4 个更专业的后代,您是对的,TCustomRichView 不应该在实际应用程序中使用。
  • TRVEditFrame 是我正在开发的组件。

我的组件背后的想法是创建一个带有菜单、快捷方式、弹出菜单等的单个框架(因此选择组件 TFrame)来管理 4 个 TCustomRichView 后代中的每一个。

这正是我使用 TCustomRichView 的原因:我可以将 4 个后代中的任何一个“插入”到我的组件框架中。这与 TDatasource 的原理相同,可以与 TTAable 和 TQuery 连接(它们具有相同的祖先)。

我想 VCL 没有将 RVEditor 链接到我在设计时设置的 TCustomRichView 后代的原因是因为 TFrame 没有 OnCreate 事件,例如 TForm 。

到目前为止,我设法通过在托管 TRVEditFrame 的 TForm.OnCreate 中手动调用 TRVEditFrame.SetRVEditor 解决了这个问题,但我想知道是否有更好的方法可以做到这一点,这就是我在这里咨询建议的原因。

我知道您也可以为 TFrame 创建一个 OnCreate 事件,也许我可以将 TRVEditFrame.SetRVEditor 放在那里,但是,我想知道是否有更好的方法。

关于您评论的最后一部分,我知道注册程序,但考虑到该组件正在开发中。当我开发组件时,我从不将它们安装在 IDE 中,因为我更喜欢将测试内容保留在“官方”之外。我使用这种方法,一旦组件准备好,我就会使用您提到的程序注册它。如果我想为同一个组件实现其他功能,我可以在测试功能上工作,同时继续使用我在 IDE 中拥有的“官方”功能。

4

2 回答 2

0

您应该创建子组件。

constructor TRVEditFrame.Create(AOwner: TComponent);
begin
    inherited; // Create(AOwner);
    FRVEditor := TRichView.Create(self);
end;

DFM 流引擎可能会加载已创建类的属性,但它无法为您创建类,原因有两个:

1) DFM 不知道对创建的组件进行的任何特殊调整,例如构造函数参数应该是什么(如果有的话),使用什么构造函数(许多),附加哪些通知和事件处理程序等等。

2) DFM 无法知道属性应该是哪种类型。例如,我发布了TStringsoroperty - 应该创建哪个类的对象来感受?TStrings? TStringList? TRichEditStringList? THashedStringList? 它们都是从那里继承而来的TStrings,因此它们中的任何一个都是 DFM 的好选择 - 但不适用于您编写的组件。

因此 DFM 流子系统负责保存和加载对象的属性,但创建这些对象完全是您的责任。


RVEdit我相信您也可以通过学习 IDE Designer 如何创建表单并创建变量而不是属性来偷工减料:

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 := ...并导致内存泄漏和编辑器和框架之间所有附加连接的意外故障。

于 2013-07-23T08:19:12.843 回答
0

我的建议是更改设计,以便使用 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 数据成员。

于 2013-07-23T17:39:37.033 回答