delphivcltform

Delphi - is it possible to disable Delphi's lazy loading of forms?


I've heard that the Delphi application uses "lazy loading", deferring the loading of form components until they are actually referenced. It was mentioned in another post - "That's why we changed TPageControl to be lazy-load - the Delphi IDE's options dialog was taking too long to load!"

I assume this applies to applications created with Delphi as well, but I can't find any mention of lazy loading in the VCL sources, suggesting maybe it's called something else if it does exist.

In cases where in normal use an application is started infrequently and runs for a long time, it might be desirable to forgo a faster startup time and have faster painting of VCL components when they are actually used for the first time.

Does the Delphi programmer have any control over this? (LazyLoad := false ; didn't work ;-)


Solution

  • Consider the following simple demonstration project:

    Project1.dpr

    program Project1;
    
    uses
      Vcl.Forms,
      Unit1 in 'Unit1.pas' {Form1};
    
    {$R *.res}
    
    begin
      Application.Initialize;
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.
    

    Unit1.pas

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
      Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls;
    
    type
      TButton = class(Vcl.StdCtrls.TButton)
      protected
        procedure CreateWnd; override;
      end;
    
      TForm1 = class(TForm)
        PageControl1: TPageControl;
        TabSheet1: TTabSheet;
        TabSheet2: TTabSheet;
        TabSheet3: TTabSheet;
        Button1: TButton;
        Button2: TButton;
        Button3: TButton;
        procedure FormCreate(Sender: TObject);
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TButton.CreateWnd;
    begin
      inherited;
      Writeln('Window created: ' + Name);
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      i: Integer;
    begin
      AllocConsole;
    end;
    
    end.
    

    Unit1.dfm

    object Form1: TForm1
      Caption = 'Form1'
      ClientHeight = 299
      ClientWidth = 635
      OnCreate = FormCreate
      PixelsPerInch = 96
      TextHeight = 13
      object PageControl1: TPageControl
        Left = 40
        Top = 40
        Width = 537
        Height = 233
        ActivePage = TabSheet1
        object TabSheet1: TTabSheet
          Caption = 'TabSheet1'
          object Button1: TButton
            Caption = 'Button1'
          end
        end
        object TabSheet2: TTabSheet
          Caption = 'TabSheet2'
          object Button2: TButton
            Caption = 'Button2'
          end
        end
        object TabSheet3: TTabSheet
          Caption = 'TabSheet3'
          object Button3: TButton
            Caption = 'Button3'
          end
        end
      end
    end
    

    When you run this, the console window says:

    Window created: Button1
    

    As you select each page in turn, the other buttons are created, as shown in the console window:

    Window created: Button1
    Window created: Button2
    Window created: Button3
    

    Now change the OnCreate event handler to force each page to be visible when the form is created:

    procedure TForm1.FormCreate(Sender: TObject);
    var
      i: Integer;
    begin
      AllocConsole;
    
      for i := 0 to PageControl1.PageCount-1 do begin
        PageControl1.Pages[i].Visible := True;
      end;
    end;
    

    Now when the form is first shown, the console window reads:

    Window created: Button1
    Window created: Button2
    Window created: Button3
    

    This is because, as Danny says, the windows are not created until they are shown.

    Now, the nuance with regards page controls is the handling of visibility of the pages. The constructor of TTabSheet contains this:

    Visible := False;
    

    Further, the Visible property of TTabSheet is published like this:

    property Visible stored False;
    

    That means that when a page control starts its life, its pages are hidden, in the VCL sense of having Visible equal to False. As Danny said, window controls are first created when a control is shown. That happens inside TWinControl.UpdateShowing which begins like this:

    procedure TWinControl.UpdateShowing;
    var
      ShowControl: Boolean;
      I: Integer;
    begin
      ShowControl := (FVisible and (not (csDesigning in ComponentState) or not (csDesignerHide in ControlState)) or
        ((csDesigning in ComponentState) and not (csDesignerHide in ControlState)) and
        not (csNoDesignVisible in ControlStyle)) and
        not (csReadingState in ControlState) and not (csDestroying in ComponentState);
      if ShowControl then
      begin
        if WindowHandle = 0 then CreateHandle; // <-- this is the key
        if FWinControls <> nil then
          for I := 0 to FWinControls.Count - 1 do
            TWinControl(FWinControls[I]).UpdateShowing;
      end;
      ....
    end;
    

    The pages start out not showing, and then when they become active in TPageControl.ChangeActivePage the following is executed for the newly active page:

    Page.BringToFront;
    Page.Visible := True;
    

    Setting Visible to True results in TWinControl.UpdateShowing executing, and the window handle being created.

    And that's why the trick above of making all the pages visible at form creation time has the effect you desire.

    Now, all of the above is very page control centric. For many other controls, the window is first created when the form is created, if the control is visible. If you a specific problem with a specific form then it would be best to share the specific details.