Search code examples
formsdelphidelphi-xe6

Delphi Form Creation and Showing


I am trying to create an application with 2 forms in Delphi XE6. Depending on a ParamStr setting Form2 may or may not be shown before Form1.

In a quick test app both forms are created before Form1.Show is invoked - during which Form2 is shown or not

procedure TForm1.FormShow(Sender: TObject);
begin
  if ParamStr(1) = 'foo' then
      Form2.FooShow;
end;

procedure TForm2.FooShow;
begin
   ShowModal;
end;

However in the "real" application I am seeing a different behaviour.

In this case Form1.Show is being called as soon as Application.CreateForm(TForm1, Form1) is called. This is before Form2 is being created, which is causing problems as Form2 doesn't exist when it is needed.

Any explanation why the behaviour would differ? Am I missing a setting buried somewhere in Project>Options


Solution

  • If your form is persisted with Visible set to True, then it will be shown as soon as it's created.

    Setting the property to False should resolve your problem.

    EDIT

    PS: Just in case someone with a similar problem has their main form unexpectedly show even though Visible is set to False. This happens because by default the application will show the main form regardless of its Visible property in the call to Application.Run.
    If so, the following question should help: How can I start Delphi application with the hidden main form?

    EDIT2

    For the sake of completeness, there are a couple other things that could cause a form to be shown as soon as it's created. However, these probably aren't applicable to this specific question.

    • Any code that explicitly shows the form when it's created (such as the OnCreate event) would obviously cause the form to be shown. However, one would hope that such actions don't lead to these kinds of questions.
    • An MDI child form can never be hidden. At best it can be minimised.

    TIP

    The quickest way to finding the answer to such questions is usually just a little bit of debugging.

    • Set a break-point in your FormShow method.
    • Go to Compiler Settings, and enable the option to build with debug DCU's. (You'll want to see the VCL code.)
    • Rebuild and run your application.
    • When you get to your break-point, open the Call Stack debug window.
    • Navigate the call-stack looking for the trigger.

    In this case you should have found the following code in Forms.pas.

    procedure TCustomForm.DoCreate;
    begin
      //...
      if fsVisible in FFormState then Visible := True; //<-- The trigger
    end;
    

    And a little more investagation on fsVisible would reveal the root cause as: The Visible property is set to True.


    That said, you don't want to be coding this way because you're creating dependencies via globals. This is error-prone; and your little experiment shows shows just one of many subtle things that can cause problems.

    Rather avoid the globals with something like the following changes in your DPR:

    begin
      Application.Initialize;
      ShowForms;
    end;
    

    Where ShowForms is implemented as:

    procedure ShowForms;
    var
      LForm1: TForm1;
      LForm2: TForm2;
    begin
      Application.CreateForm(TFrom1, LForm1);
      Application.CreateForm(TFrom2, LForm2);
    
      if (ParamStr(1) = 'foo') then
        LForm2.Show
      else
       LForm1.Show;
    end;
    

    If you don't have any dependencies between the forms, the above will suffice. If you do have a dependency, e.g. Form2 uses Form1: then you can explicitly pass a reference after creating the forms, bu before you start doing anything with them.

    //Define a property on TForm2 E.g.
    property MainForm: TForm1 read FMainForm write SetMainForm;
    
    //Immediately after creating both forms tell form2 which to use as its main form.
    LForm2.MainForm := LForm1;