我有一些函数要在我的应用程序完成初始化和主窗体创建后执行。我确实有窗体OnShow事件中的代码(称之为ProcedureX),但我刚刚注意到它被调用了两次,因为OnShow被触发了两次。当主程序DPR调用时,它会触发:
Application.CreateForm(TMainForm, MainForm) ;
但是在那之后,当我从INI文件中读取包含窗体在屏幕上的位置的内容时,我会调用:
MainForm.position := poScreenCenter ;
这似乎会再次触发OnShow事件。
我可以在哪里调用ProcedureX,它只能被调用一次,并且在执行之前需要创建主窗体?
7条答案
按热度按时间cbeh67ev1#
如果您的程式码只需要在每次建立表单时执行一次(或在每次执行应用程序时只会建立一次表单),请将程式码放在表单的OnCreate行程常式中。这是程式码的自然位置。
现在(我认为是从D3开始)OnCreate在AfterConstruction方法中的构造过程结束时触发。只有当您将OldCreateOrder设置为True(默认为False)时,您可能会遇到麻烦,因为这会使OnCreate在Create构造函数结束时触发。
qlzsbp2j2#
表单的正常执行顺序为:
AfterConstruction
:当表单和它的组件及其所有属性完全创建时。OnShow
:当表单准备好显示时(是的,任何导致CM_SHOWINGCHANGED
的更改都可以触发OnShow
)Activate
:每当窗体获得焦点时因此,根据ProcedureX中的需要,
AfterConstruction
可能就足够了,并且只执行一次;只需覆盖它并在inherited
之后添加ProcedureX。它将在OnCreate
之后。如果不是这种情况,您可以从
AfterConstruction
将自定义消息发布到窗体,该消息将排队,并在处理完其他消息后到达您的自定义处理程序。在这两种情况下,您都不需要额外的布尔字段。
qkf9rpyu3#
@舍泰克,
实际上不需要FRUNOnce字段;只需将OnShow=NIL作为FormShow方法的第一行即可。
仅供参考,“运行一次”习惯用法--在事件处理程序的第一行将事件处理程序字段设置为NIL--对于在窗体完全初始化后启动并运行某些代码也非常有用。将代码放在FormActivate方法中,并在该方法的第一行设置OnActivate=NIL。
i86rm4rw4#
第一次调用过程时,可以测试并设置一个标志。如下所示:
ghhaqwfi5#
您可以在DPR文件中的Application.CreateForm之后添加一个过程。将需要初始化的所有代码放入该过程中。当您的应用程序中有多个窗体时效果最佳。
此外,如果初始化需要很多时间,它会让程序在屏幕上显示表单,这样用户就可以知道应用程序正在加载。
示例:
kzipqqlq6#
我将对this answer by Server Overflow提出一个稍微不同的方法。我们将实现几乎完全相同的效果,但在DPR文件(主项目源文件)中没有任何编辑。我们将通过在主窗体单元中使用类助手来实现:
注意,Unit1.MainForm.PreRun是主窗体中的某个方法,只有一点需要注意:如果主窗体名为“MainForm,”则需要在helper的方法中为其添加单元名前缀,因为TApplication类已经有一个名为MainForm的成员。顺便说一句,如果确实省略了前缀,这可能仍然有效,因为Unit1.MainForm确实也是应用程序的主窗体。
之所以这样做,是因为Unit 1在DPR项目的uses列表中,并且只要TAppHelper在接口部分(而不是在实现部分)中定义,它就会被加载,并且在DPR文件中调用Application.Run方法时,它已经是它的helper版本了。
它的美妙之处在于,它只会运行一次,而且是在所有窗体都已经创建,所有构造函数都已经执行之后。事实上,我们在DPR文件中有效地自定义了Application.Run调用,而不需要编辑DPR文件,这是一种巧妙的做法。同样, Delphi /lazarus中的类助手!
我将分享一个更巧妙的窍门,先看一看:
每当我想让代码在执行时有一点延迟时,我都会使用这个技巧。为什么?因为如果代码在继承的Run方法之前运行,它可能会(取决于代码内部发生了什么)暂时挂起UI,但时间要足够长,以便窗体在启动过程中闪烁并显示为没有响应。此外,我们不能简单地将代码放在继承的Run方法后面,因为在应用程序被终止之前它不会被执行。所以我使用System.Threading单元中的TTask。sleep(10)可能有点过头了,sleep(1)最有可能完成这项工作,甚至可能根本不休眠也行,但是我在那里做了一些复杂的初始化,所以我保持了很大的延迟。额外的好处:如果你不从你的PreRun自定义方法更新UI,那么你甚至不需要TThread.Synchronize Package 器,这会变得更简单。在FPC/Lazarus的情况下,你可以通过使用TApplication.QueueAsyncCall()而不是TTask类来实现同样的效果。
我真的认为这是一个巧妙的技巧,因为我可以完全在DPR文件之外编写它,在定义PreRun方法的窗体单元中,并且在所有窗体都已经创建之后,而不仅仅是在我实现PreRun方法的窗体单元中,它是有保证的。另外,如果类助手在窗体单元中,而不是在其他地方,那么PreRun甚至不需要是公共的,它也可以和受保护的方法甚至私有方法一起工作!2这对于使这个小逻辑远离代码的任何其他部分是很好的。
x6h2sr287#
@赛泰克,
如果您想让代码为每个unhide事件运行,那么您的代码也不会工作(您没有放入任何代码来重置frunonce字段)。
因此,您的方法需要重置frunonce字段,而我的方法需要设置OnShow=FormShow。相同的区别,只是您需要一个额外的字段。