【问题标题】:What is called after Loaded() in DelphiDelphi中Loaded()之后叫什么
【发布时间】:2010-09-23 00:48:50
【问题描述】:

我有一些代码可以在 Loaded() 函数中设置一些内部对象。但是,一些外部对象还没有完全创建,而是在 Loaded() 函数完成之后。 Delphi调用Loaded()后调用了什么函数?

更好的是,组件的创建顺序是什么?

基本上我有一个 TCP 服务器和客户端。大多数人会将这两个组件放在两个单独的应用程序中,有些人会将它们放在同一个应用程序中以供本地访问。

我的客户端尝试在 OnLoaded() 中从服务器获取数据,但服务器可能尚未启动!我想知道在调用了所有 OnLoaded() 之后是否调用了另一个函数。

【问题讨论】:

  • 如果您正在编写一个拥有并创建服务器和客户端组件的组件,请使用组件本身的负载,而不是服务器或客户端的负载。此 Load 方法可以负责配置和“打开”服务器,使其正在侦听,然后连接客户端并拉取所需的信息。

标签: delphi vcl


【解决方案1】:

在 dfm 流式传输后立即调用 Loaded,不应使用它来访问服务器。您最好的选择可能是在构造函数中向自己发布一条自定义消息,并拥有一个响应该消息的消息处理程序。发布消息会将其放入消息队列的末尾,因此在处理完之前的所有其他消息之前,它不会被处理。这应该会延迟足够长的时间,以便您的组件完全构建以供使用。

【讨论】:

  • Bam,这就是问题所在,服务器是在主窗体中创建的,客户端是其中一个组件的内部。我无法确保首先创建服务器。缺少一个在 Loaded() 之后调用的函数,向自己发送一条消息是下一个最好的事情。谢谢。
【解决方案2】:
  1. 我在某些组件中使用了断点,并确定 AFTERCONSTRUCTION 称为 BEFORE LOADED 而不是 AFTER。

  2. 我也在 FORM 上做了同样的事情,并确定 AFTERCONSTRUCTION 被称为 AFTER LOADED 而不是 BEFORE。

请记住,AfterConstruction 是 TObject 中的一个方法,但 Loaded 不是。由此可见,Loaded 是由不一定按相对于 AfterConstruction 的特定顺序的代码生成的,因为 Loaded 实际上不是 TObject 的构造序列的一部分,而 AfterConstruction 是。

确实,如果您研究 RTL 源代码,您会发现 Loaded 甚至没有被 TComponent 的任何 self.method 调用,而是由正在读取 DFM 的流读取器调用,而且很可能在“所有者”组件的控制下发生。因此,我强烈建议它与 AfterConstruction 执行的关系并不能真正得到保证。它以特定顺序出现在表单中的事实是因为表单最有可能是启动流读取的组件。换句话说,Loaded 在表单中的 AfterConstruction 之前有点意外。

进一步的研究表明,包含以下代码的 NON-FORM 组件可能永远不会调用事件处理程序。

procedure Txxx.AfterConstruction; override;
begin
   inherited AfterConstruction;
   if Assigned(FOnCreate) then FOnCreate(Self);
end;

原因是AfterConstruction,如果在属性加载之前调用,会发现FOnCreate还没有赋值!

在这种情况下,你真的必须使用以下方法:

procedure Loaded; override;
begin
   inherited Loaded;
   if assigned(OnLoaded) then OnLoaded(self);
end;

就像我说的,这会为表单拥有的组件产生与表单本身不同的结果! TForm 组件通常是 DFM 流读取器的调用者,它是流读取器为它从表单中读取的每个组件调用 Loaded。这个过程(幸运的是)在表单的 AfterConstruction 之前开始,但是由该阅读器加载的每个组件都会在其加载的方法之前调用其 AfterConstruction 方法。

QED。

具有讽刺意味的是,Delphi 6 帮助文件说“在 TObject 中实现的 AfterConstruction 方法什么都不做。当创建一个在创建对象后执行某些操作的类时重写此方法。例如,TCustomForm 重写 AfterConstruction 以生成一个OnCreate 事件。”

它没有说明的是,如果你在 TCustomForm 以外的任何东西上尝试这个(它已经这样做了),它就不起作用!因为只有一个表单(已经拥有它)会在调用 AfterConstruction 之前加载它的 OnCreate 属性。任何其他组件都不会,因为表单调用的 DFM 读取器在加载之前调用 AfterConstruction! Borland 等人的一个明显案例。人。不理解他们自己的代码,或者充其量是编写一个帮助文件条目,暗示某事是可能的,而实际上并非如此。

注意,如果您的组件不在表单上并且是在运行时创建的(即使这是作为“拥有”组件),则不会调用其“Loaded”方法,因为不涉及流读取器。

另一个有趣的地方是“Dr”Bob Swart 前段时间写的关于 AfterConstruction 的内容,即它代表了可以调用虚方法的点。显然这只是部分正确:如果在 AfterConstruction 之前调用表单的 Loaded 方法,那么如果这是真的,您将无法从 Loaded 调用任何虚拟方法。情况并非如此(显然),因为 Loaded 本身就是一个虚拟方法!显然,流读取器在构造函数和 AfterConstruction 之间调用表单的 Loaded。这就引出了一个问题:流阅读器实际上是通过什么方法调用的?我的猜测是它在应用程序(而不是表单)的控制下运行,并且它故意为表单而不是其他组件调用 AfterConstruction,或者它是表单的构造函数在创建 VMT 之后所做的最后一件事,因此在表单中调用 AfterConstruction 之前发生的最后一件事。因此,在调用表单的 AfterConstruction 之前,调用表单拥有的所有 AfterConstruction-Loaded 组件对联。跟踪调用还表明,在调用所有这些组件的所有加载方法之前,大多数情况下都会为所有这些组件调用 AfterConstruction。但是,我没有测试存在分层“父级”(例如带有组件的面板)的情况,因此可能会有变化。

【讨论】:

    【解决方案3】:

    通常您会为此目的覆盖 TObject.AfterConstruction。

    执行顺序为:

      each Component.AfterConstruction in creation order
    (Form or DataModule).Loaded  
      each Component.Loaded in creation order
    (Form or DataModule).AfterConstruction 
    

    追踪:

    Debug Output: button AfterConstruction Process Project2.exe (4876)
    Debug Output: Form Loaded Process Project2.exe (4876)
    Debug Output: button Loaded Process Project2.exe (4876)
    Debug Output: Form AfterConstruction Process Project2.exe (4876)
    

    【讨论】:

    • AfterConstruction 在 OnLoaded 之前被调用,所以我需要的组件也没有准备好。我需要在主窗体中的所有组件上调用 Loaded 之后调用的东西。似乎什么都没有。
    • 我不知道任何标准的onLoaded 事件。很抱歉,Self.Loaded 发生在 Self.AfterConstruction 之前。
    • 您可能有一些动态创建组件的代码(即不是 dfm 的一部分),在这种情况下,它们的 Loaded/AfterConstruction 可能会在所有者表单的该序列之后发生。但是如果它们在 Form 的 Loaded 例程中已经准备好,那么在 AfterConstruction 中就更是如此了。
    • 我在 OnLoaded 和 AfterConstruction 上都设置了断点。 AfterConstruction 断点首先被命中。每次。
    • 请更改标题并重新表述您的问题。这与我的答案和 cmets 正确的 TComponent 的 Loaded 和 AfterConstruction 的执行顺序无关。尝试在表单的 Loaded 和 AfterConstruction 方法中放置一个断点......我很想看看你在“OnLoaded”中放置一个断点的代码,因为在 Delphi 源代码中没有这样的东西。
    【解决方案4】:

    我不知道你的意思是什么

    服务器可能还没有启动

    无论如何,如果客户端和服务器都在同一个应用程序表单或数据模块上,我看到了替代方案:

    1. 您可以“强制”系统在客户端之前创建服务器,并在服务器的 OnLoad 中启动服务器,它会启动 在客户端 OnLoad,因为documentation 说:

      当流系统从表单文件加载表单或数据模块时,它首先通过调用其构造函数来构造表单组件,然后从表单文件中读取其属性值。在读取所有组件的所有属性值后,流系统按照组件的创建顺序调用每个组件的 Loaded 方法。这使组件有机会初始化依赖于其他组件或自身其他部分的值的任何数据。

    2. 每当服务器启动时通知“客户端”以使其初始化(从服务器拉数据)。您可以使用直接方法调用、发布消息或任何您觉得舒服的方式。

    3. 让客户端站起来服务器在它自己的 OnLoad 方法中。

    【讨论】:

    • 正如我在回答中所说,在 Loaded 阶段完成后,Form 或 DataModule 的 AfterConstruction 就会发生。如果服务器已经在侦听(可能与 Loaded 不同),则客户端应尝试连接到服务器的位置
    • @François:编辑答案后,很明显您在谈论表单/数据模块 AfterConstruction。在我不清楚之前。
    【解决方案5】:

    为什么不使用主窗体的 onCreate 事件?

    【讨论】:

    • 这让组件的用户有责任了解该组件的内部工作以进行设置。不是我想要的
    猜你喜欢
    • 2019-07-01
    • 2014-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-26
    相关资源
    最近更新 更多