Delphi 应用程序完成初始化后,我应该在哪里放置要执行的代码?

bd1hkmkf  于 2022-11-04  发布在  其他
关注(0)|答案(7)|浏览(218)

我有一些函数要在我的应用程序完成初始化和主窗体创建后执行。我确实有窗体OnShow事件中的代码(称之为ProcedureX),但我刚刚注意到它被调用了两次,因为OnShow被触发了两次。当主程序DPR调用时,它会触发:

Application.CreateForm(TMainForm, MainForm) ;

但是在那之后,当我从INI文件中读取包含窗体在屏幕上的位置的内容时,我会调用:

MainForm.position := poScreenCenter ;

这似乎会再次触发OnShow事件。
我可以在哪里调用ProcedureX,它只能被调用一次,并且在执行之前需要创建主窗体?

cbeh67ev

cbeh67ev1#

如果您的程式码只需要在每次建立表单时执行一次(或在每次执行应用程序时只会建立一次表单),请将程式码放在表单的OnCreate行程常式中。这是程式码的自然位置。
现在(我认为是从D3开始)OnCreate在AfterConstruction方法中的构造过程结束时触发。只有当您将OldCreateOrder设置为True(默认为False)时,您可能会遇到麻烦,因为这会使OnCreate在Create构造函数结束时触发。

qlzsbp2j

qlzsbp2j2#

表单的正常执行顺序为:

  • AfterConstruction:当表单和它的组件及其所有属性完全创建时。
  • OnShow:当表单准备好显示时(是的,任何导致CM_SHOWINGCHANGED的更改都可以触发OnShow
  • Activate:每当窗体获得焦点时

因此,根据ProcedureX中的需要,AfterConstruction可能就足够了,并且只执行一次;只需覆盖它并在inherited之后添加ProcedureX。它将在OnCreate之后
如果不是这种情况,您可以从AfterConstruction将自定义消息发布到窗体,该消息将排队,并在处理完其他消息后到达您的自定义处理程序。
在这两种情况下,您都不需要额外的布尔字段。

qkf9rpyu

qkf9rpyu3#

@舍泰克,
实际上不需要FRUNOnce字段;只需将OnShow=NIL作为FormShow方法的第一行即可。
仅供参考,“运行一次”习惯用法--在事件处理程序的第一行将事件处理程序字段设置为NIL--对于在窗体完全初始化后启动并运行某些代码也非常有用。将代码放在FormActivate方法中,并在该方法的第一行设置OnActivate=NIL。

i86rm4rw

i86rm4rw4#

第一次调用过程时,可以测试并设置一个标志。如下所示:

type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
  private
    FRunOnce: Boolean;
  public
    [...]

[...]

procedure TForm1.FormShow(Sender: TObject);
begin
  if not FRunOnce then begin
    FRunOnce := True;
    ProcedureX;
  end;
end;
ghhaqwfi

ghhaqwfi5#

您可以在DPR文件中的Application.CreateForm之后添加一个过程。将需要初始化的所有代码放入该过程中。当您的应用程序中有多个窗体时效果最佳。
此外,如果初始化需要很多时间,它会让程序在屏幕上显示表单,这样用户就可以知道应用程序正在加载。
示例:

PROGRAM MyProgram;
begin
    Application.Initialize;
    Application.CreateForm(TMyForm, MyForm);
    MyForm.Show;

    LateInitialize;        <----------- here

    Application.Run;
end.
kzipqqlq

kzipqqlq6#

我将对this answer by Server Overflow提出一个稍微不同的方法。我们将实现几乎完全相同的效果,但在DPR文件(主项目源文件)中没有任何编辑。我们将通过在主窗体单元中使用类助手来实现:

type
{ TAppHelper }
  TAppHelper
  = Class helper for TApplication
      Public Procedure Run;
    End;

  Procedure TAppHelper.Run;
    begin
      Unit1.MainForm.PreRun;
      inherited Run;
    end;

注意,Unit1.MainForm.PreRun是主窗体中的某个方法,只有一点需要注意:如果主窗体名为“MainForm,”则需要在helper的方法中为其添加单元名前缀,因为TApplication类已经有一个名为MainForm的成员。顺便说一句,如果确实省略了前缀,这可能仍然有效,因为Unit1.MainForm确实也是应用程序的主窗体。
之所以这样做,是因为Unit 1在DPR项目的uses列表中,并且只要TAppHelper在接口部分(而不是在实现部分)中定义,它就会被加载,并且在DPR文件中调用Application.Run方法时,它已经是它的helper版本了。
它的美妙之处在于,它只会运行一次,而且是在所有窗体都已经创建,所有构造函数都已经执行之后。事实上,我们在DPR文件中有效地自定义了Application.Run调用,而不需要编辑DPR文件,这是一种巧妙的做法。同样, Delphi /lazarus中的类助手!
我将分享一个更巧妙的窍门,先看一看:

Procedure TAppHelper.Run;
    begin
      TTask.Run(
        procedure
          begin
            sleep(10);
            TThread.Synchronize(nil, procedure begin Unit1.MainForm.PreRun; end);
          end
        );
      inherited Run;
    end;

每当我想让代码在执行时有一点延迟时,我都会使用这个技巧。为什么?因为如果代码在继承的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这对于使这个小逻辑远离代码的任何其他部分是很好的。

x6h2sr28

x6h2sr287#

@赛泰克,
如果您想让代码为每个unhide事件运行,那么您的代码也不会工作(您没有放入任何代码来重置frunonce字段)。
因此,您的方法需要重置frunonce字段,而我的方法需要设置OnShow=FormShow。相同的区别,只是您需要一个额外的字段。

相关问题