我有一些代码可以在Loaded()函数中对内部对象进行一些设置。但是,某些外部对象尚未完全创建,但是在Loaded()函数完成之后。 Delphi在调用Loaded()之后调用了什么函数?
更好的是组件的创建顺序是什么?
基本上我有一个TCP服务器和客户端。大多数人会将这两个组件放在两个独立的应用程序中,有些会将它们放在同一个应用程序中进行本地访问。
我的客户端尝试在OnLoaded()中从服务器获取数据,但服务器可能尚未启动!我想知道在调用所有OnLoaded()之后是否调用了另一个函数。
答案 0 :(得分:3)
在流式传输dfm后立即调用Loaded,不应使用它来访问服务器。您最好的选择可能是在构造函数中向自己发布自定义消息,并具有响应该消息的消息处理程序过程。发布消息会将其放入消息队列的末尾,因此在处理完所有其他消息之前,它将不会被处理。这应该延迟足够长的时间,以便您的组件完全构建起来使用。
答案 1 :(得分:2)
通常你会为此目的覆盖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)
答案 2 :(得分:2)
我在某些组件中使用了断点,并坚定地确定AFTERCONSTRUCTION被称为BEFORE LOADED而不是之后。
我也在FORM上做了同样的事情,并且坚定地确定AFTERCONSTRUCTION被称为AFTER LOADED之前没有。
请记住,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之前,但是该读取器加载的每个组件都获得了它的AfterFonstruction方法,称为BEFORE其加载方法。
QED。
讽刺的是,Delphi 6帮助文件说“在TObject中实现的AfterConstruction方法没有做任何事情。在创建一个在创建对象后执行某些操作的类时重写此方法。例如,TCustomForm重写AfterConstruction以生成一个OnCreate事件。“
它省略的是,如果你在TCustomForm(已经有它)之外的任何东西上尝试这个,它就不起作用了!因为只有一个表单(已经有它)将在调用AfterConstruction之前加载其OnCreate属性。任何其他组件都不会,因为表单调用的DFM读取器在加载之前调用AfterConstruction! Borland等人的一个明显案例。人。不理解他们自己的代码,或者充其量,编写一个暗示某些东西的帮助文件条目,实际上它不是。
注意,如果您的组件不在表单上并且是在运行时创建的(即使它是“拥有”组件),则不会调用其“Loaded”方法,因为没有涉及流阅读器。
另一个兴趣点是“Dr”Bob Swart不久前写过关于AfterConstruction的内容,即它代表了可以调用虚方法的点。显然这只是部分正确:如果在AfterConstruction之前调用了一个表单的Loaded方法,那么如果这是真的,你将无法从Loaded调用任何虚方法。事实并非如此(显然)因为Loaded本身就是一种虚拟方法!显然,流读取器在构造函数和AfterConstruction之间调用表单的加载。它提出了一个问题:实际调用流阅读器的方法是什么?我的猜测是它在应用程序(而不是表单)的控制下运行,并且它故意为表单调用AfterConstruction而不是其他组件,或者它是表单构造函数在创建VMT后所做的最后一件事,因此在表单中调用AfterConstruction之前发生的最后一件事。因此,在调用表单的AfterConstruction之前,将调用表单所拥有的所有AfterConstruction-Loaded对联组件。跟踪调用还表明,在调用所有加载的方法之前,所有这些组件都会调用AfterConstruction。然而,我没有测试存在分层“父母”的情况(例如带有组件的面板),因此可能存在变化。
答案 3 :(得分:1)
我不确定你的意思
服务器可能尚未启动
无论如何,如果客户端和服务器都在同一个申请表或数据模块上,我会看到其他选择:
您可以“强制”系统在客户端之前创建服务器,并在服务器的OnLoad中启动服务器,它将 up 在客户端OnLoad上,因为documentation说:
当流系统从其表单文件加载表单或数据模块时,它首先通过调用其构造函数构造表单组件,然后从表单文件中读取其属性值。在读取所有组件的所有属性值之后,流式系统按照创建组件的顺序调用每个组件的Loaded方法。这使组件有机会初始化任何依赖于其他组件或其他部分的值的数据。
每当服务器启动时通知“客户端”以使其初始化(从服务器提取数据)。您可以使用直接方法调用,发布消息或任何您认为合适的方式。
让客户端在其自己的OnLoad方法中站起来服务器。
答案 4 :(得分:1)
为什么不使用主窗体的onCreate事件?