Jump to content
egnew

Set Parent of Component to Application.MainForm from Unit

Recommended Posts

I populate forms dynamically from units.  Attempts to create a TForm component on the Application.MainForm but has failed.  My current workaround is to drop a "Host" panel on the MainForm and use it as the parent.


procedure MakeSurface;

MyComponent[0] := TPanel.Create;

TPanel(MyComponent[0]).Parent := Application.MainForm;

MyParent := MyComponent[0];

 

procedure MakeTopPanel

MyComponent[1] := TPanfel.Create;

TPanel(MyComponent[1]).Align := alTop;

TPanel(MyComponent[1].Parent := MyParent;

TPanel(MyComponent[1].Name := 'TopPanel';

 

Application.MainForm.FormCreate

inherited;

MakeSurface;

MakeTopPanel;

This fails with the message "Control 'TopPanel' has no parent window.  Path: TopPanel."

 

Application.MainForm.FormCreate

inherited;

MakeSurface;

MyComponent[0].Parent := Self;

MakeTopPanel;

This works without a problem.

 

 

 

Share this post


Link to post

A simple debug would tell you the reason. At the moment of Application.MainForm.Formcreate the Application.MainForm property is nil. 

  • Like 1
  • Thanks 1

Share this post


Link to post

Lajos Juhász - Thanks.

 

I called inherited in the OnCreate event and never thought Delphi would set the MainForm property later.   I moved the code to OnShow, and it works fine now.

 

Thanks

 

Edited by egnew

Share this post


Link to post
2 minutes ago, egnew said:

I called inherited in the OnCreate event and never thought Delphi would set the MainForm property later.   I moved the code to OnShow, and it works fine now.

I wouldn't do that. You or user can call an OnShow event repeatedly. It will create objects each time.

Share this post


Link to post

TApplication.CreateForm in the first place must create the instance (when the formcreate is called) and just after can assign it to mainform.

 

You can do

 

TMainForm.FormCreate(Sender: TObject);

begin

 .....

  MakeSurface(self);

 

For me it's always a code smell when somebody uses a global variable or reference.

  • Like 1

Share this post


Link to post
On 3/24/2023 at 10:50 AM, Stano said:

I wouldn't do that. You or user can call an OnShow event repeatedly. It will create objects each time.

This is not an issue because the first statement in the OnShow event is "OnShow := nil;" which keeps it from being called again.  I would never call the OnShow event explicitly.

 

Share this post


Link to post

this would works for you?

 

unit uMyControlsBeforeFormCreate;

interface

uses
  System.SysUtils,
  System.Classes,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.StdCtrls,
  Vcl.ExtCtrls,
  Vcl.Dialogs;

procedure CreateMyPanelFromDPR(AOwner: TComponent; AParent: TWinControl; APanelName: string);
procedure ComponentsOnForm1(const AForm: TForm);

implementation

procedure CreateMyPanelFromDPR(AOwner: TComponent; AParent: TWinControl; APanelName: string);
var
  APanel: TPanel;
begin
  if (AOwner = nil) then
    AOwner := Application;
  //
  if (AParent = nil) then
    AParent := Application.MainForm;
  //
  if (APanelName.IsEmpty) then
    APanelName := 'PanelHello';
  //
  APanel        := TPanel.Create(AOwner);
  APanel.Name   := APanelName;
  APanel.Parent := AParent;
  //
  APanel.Left   := 10;
  APanel.Top    := 10;
  APanel.Width  := 200;
  APanel.Height := 200;
  //
  APanel.Caption := AOwner.ToString + AParent.ToString;
end;

procedure ComponentsOnForm1(const AForm: TForm);
var
  LMemo: TComponent;
begin
  if (AForm = nil) and not(AForm is TForm) then
    exit;
  //
  LMemo := AForm.FindComponent('Memo1');
  //
  if (LMemo <> nil) and (LMemo is TMemo) then
    begin
      TMemo(LMemo).Text := 'Application components';
      //
      for var i: integer := 0 to (Application.ComponentCount - 1) do
        TMemo(LMemo).Lines.Add(Application.Components[i].ToString);
      //
      TMemo(LMemo).Lines.Add(slinebreak + 'Form1 components');
      //
      for var i: integer := 0 to (AForm.ComponentCount - 1) do
        TMemo(LMemo).Lines.Add(AForm.Components[i].ToString);
      //
      TMemo(LMemo).Lines.Add(slinebreak + 'Form1 controls');
      //
      for var i: integer := 0 to (AForm.ControlCount - 1) do
        TMemo(LMemo).Lines.Add(AForm.Controls[i].ToString);
    end;
end;

end.
program Project1;

uses
  Vcl.Forms,
  uFormMain in 'uFormMain.pas' {Form1} ,
  uMyControlsBeforeFormCreate in 'uMyControlsBeforeFormCreate.pas';

{$R *.res}

begin
  ReportMemoryLeaksOnShutdown := true;
  //
  Application.Initialize;
  Application.MainFormOnTaskbar := true;
  Application.CreateForm(TForm1, Form1);
  //
  CreateMyPanelFromDPR( { Application, Form1 } Form1, { Form1 } nil, 'PanelHello');
  //
  ComponentsOnForm1(Form1);
  //
  Application.Run;

end.
type
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormShow(Sender: TObject);
  private
    procedure MyPanelClick(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormShow(Sender: TObject);
var
  LCmp: TComponent;
begin
  LCmp := FindComponent('PanelHello');
  //
  if (LCmp <> nil) and (LCmp is TPanel) then
    TPanel(LCmp).OnClick := MyPanelClick;
end;

procedure TForm1.MyPanelClick(Sender: TObject);
begin
  ShowMessage('Hello Panel');
end;

end.

image.thumb.png.3d8bd74ff58bdf066e88cda699f3ed7d.png

 

Edited by programmerdelphi2k

Share this post


Link to post

To my knowledge, unless it's changed, OnShow is called on every new form view.
MyForm.Hide -> MyForm.Show(Modal)

Share this post


Link to post

YOU can call "Form1.FormShow(Self)" before "SHOW" your forms, to execute this event!!! 

no needs wait that it be on the screen!

var
  LFlagFormShow: boolean = true; // false  // global var no necessary good for all, then, try a property of the object!

procedure TForm1.FormShow(Sender: TObject);
begin
  if not LFlagFormShow then
    exit;
  // ...
end;

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×