如何正确发布从 'Loaded' 过程执行的事件?

How to properly publish an event executed from the 'Loaded' procedure?

在仅运行时包中,我定义了一个 TFrame 发布 OnLoaded 事件的后代:

type
  TMyMethod = procedure() of object;

  TMyFrame = class(TFrame)
  protected
    FOnLoaded : TMyMethod;
    procedure Loaded(); override;
  published
    property OnLoaded : TMyMethod read FOnLoaded write FOnLoaded;
  end;

implementation

{$R *.dfm}

procedure TMyFrame.Loaded();
begin
  inherited;
  if(Assigned(FOnLoaded))
  then FOnLoaded();
end;

在仅设计时包中,我注册了 TMyFrame 组件如下:

unit uMyRegistrations;

interface

uses
  Classes, uMyFrame;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('MyTestComponents', [
    TMyFrame
  ]);
end;

我已经安装了设计时包,我可以在工具面板中找到 TMyFrame,它的 OnLoaded 事件显示在对象检查器中。

我已将 TMyFrame 拖到一个表单中,然后通过在对象检查器中双击来分配 OnLoaded 事件。 分配事件后,我注意到每次尝试打开 Delphi 中的表单文件时都会出现访问冲突错误消息(它让我打开“.pas”文件,但我无法切换到可视化设计器查看)。

我是否正确发布了 OnLoaded 活动?如果是这样,还有什么问题?

更多信息:

  1. 我正在使用 Delphi 2007(不知道是否重要)。
  2. 对不同的父 类 做同样的事情也会出现错误(不仅对 TFrame 后代)。

已更新(少一些虚假)答案

你接受了我原来的回答,但是我写的不对。 Rob Kennedy 指出前 Embarcadero 开发人员 Allen Bauer 关于 Assigned 主题的 article

Allen 解释说 Assigned 函数只测试方法指针中两个指针中的一个指针。设计时的 IDE 通过将标记值分配给任何已发布的方法属性(即事件)来利用这一点。这些哨兵值具有 nil 用于方法指针中的两个指针之一(Assigned 检查的指针),以及标识另一个指针中的 属性 值的索引。

这一切意味着当你在设计时调用Assigned时返回False。只要您在调用它们之前使用 Assigned 检查已发布的方法指针,那么您将永远不会在设计时调用它们。

原来我写的不可能是真的

所以我挖得更深了一点。我使用了以下非常简单的代码,使用 XE7 进行测试:

type
  TMyControl = class(TGraphicControl)
  protected
    FSize: Integer;
    procedure Loaded; override;
  end;

....

procedure TMyControl.Loaded;
begin
  inherited;
  FSize := InstanceSize;
end;

....

procedure Register;
begin
  RegisterComponents('MyTestComponents', [TMyControl]);
end;

每当执行 Loaded 方法时,这足以在设计时在 IDE 中导致 AV。

我的结论是,IDE 在流式传输时做了一些相当卑鄙的事情,并且在调用 Loaded 方法时,您的对象不适合使用。但我真的没有比这更好的理解了。


原始(非常虚假)回答

您不得在设计时执行事件处理程序,而您的代码正是这样做的。原因是在设计时事件处理程序的代码不可用。

控件的代码可用,IDE 已加载它 – 但实现事件处理程序的代码不可用。该代码不是设计时包的一部分,它是当前在 IDE 中打开的项目的一部分。毕竟,它甚至可能还无法编译!

Loaded 方法应该像这样防御:

procedure TMyFrame.Loaded();
begin
  inherited;
  if not (csDesigning in ComponentState) and Assigned(FOnLoaded) then 
    FOnLoaded();
end;