egnew 5 Posted March 24, 2023 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
Lajos Juhász 295 Posted March 24, 2023 A simple debug would tell you the reason. At the moment of Application.MainForm.Formcreate the Application.MainForm property is nil. 1 1 Share this post Link to post
egnew 5 Posted March 24, 2023 (edited) 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 March 24, 2023 by egnew Share this post Link to post
Stano 143 Posted March 24, 2023 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
Lajos Juhász 295 Posted March 24, 2023 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. 1 Share this post Link to post
egnew 5 Posted March 30, 2023 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
programmerdelphi2k 237 Posted March 30, 2023 (edited) 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. Edited March 30, 2023 by programmerdelphi2k Share this post Link to post
Stano 143 Posted March 31, 2023 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
programmerdelphi2k 237 Posted March 31, 2023 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