FormCreate procedure - delphi

I have two units in my project as follow:
1 - Connexion unit:
unit Connexion;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TFConn = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
FConn: TFConn;
implementation
{$R *.dfm}
uses MainForm;
procedure TFConn.Button1Click(Sender: TObject);
begin
if not Assigned(FMain) then
begin
FMain := TFMain.CreateNew(Application);
FMain.OnClose := FMain.FormClose;
FMain.ShowModal;
end;
end;
end.
2 - MainForm unit :
unit MainForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TFMain = class(TForm)
Constructor FormCreate(Sender: TObject);overload;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
end;
Var
FMain : TFMain;
implementation
Constructor TFMain.FormCreate(Sender: TObject);
var B : TButton;
begin
inherited;
B := TButton.Create(Self);
B.Parent := Self;
B.Caption := 'Button2';
end;
procedure tfmain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FMain := Nil;
end;
end.
The problem is that the procedure FormCreate in MainForm unit not working , I know that I miss something in the declaration , because the procedure should fire while the creation of the FMain form .
The application run without any error , but it should create B button on the FMain form.
How can I do that?

Constructor FormCreate(Sender: TObject);overload;
is wrong. It should be:
procedure FormCreate(Sender: TObject);
The other problem is that you must set the OnCreate event to refer to FormCreate. Do this in the events page of the object inspector.
That will also need you to have a dfm file for this form which you appear not to have. Once you restore the dfm file you can set the OnClose event handler in the same way. You'd need to switch to Create rather than CreateNew.
You can't set the OnCreate event handler in code because the form has already been created.
If there is a good reason not to have a dfm file and do everything in code then you could need to add a constructor. Do that by overriding the virtual constructor:
constructor Create(AOwner: TComponent); override;
Finally, very much of the code in the question looks dubious, shall I saw. Strange use of global variables. Odd naming. Setting important events like OnClose from outside the class. Testing against nil that suggests weak design. I think there are problems ahead.

Related

Does VCL components have onCreate event?

Need to access some components right after program start but I have found that doing so from form's onCreate event is bad because at the moment they may still be unavailable (access violation occurs). Can not find onCreate event in any component. Am I missing something?
Here is code. Empty form with ValueListEditor.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure: Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Load;
end;
procedure Load;
begin
(Application.MainForm.FindComponent('ValueListEditor1') as TValueListEditor)
.Strings.LoadFromFile('c:\li');
end;
end.
The problem is not because your component hasn't been created yet, because it has been. The real problem is because the Application.MainForm property hasn't been assigned yet when your main Form's OnCreate event is fired, so you are calling FindComponent() on a nil Form pointer.
Since Load() is merely accessing a member of TForm1 then Load() should also be a member of TForm1 as well, and then you can call it, and thus access your component, via the implicit Self pointer, which is valid during the Form's OnCreate event, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
end.
If, for whatever reason, Load() must be a standalone procedure, then at least have it use your global Form1 variable, which the call to Application.CreateForm() will assign before the main Form's OnCreate event is fired, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
procedure Load;
begin
if Form1 <> nil then
Form1.LoadValues;
end;
end.
Alternatively, you can fallback to looking for the Form1 object in the Screen.Forms[] array, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
procedure Load;
var
I: Integer;
Frm: TForm;
begin
for I := 0 to Screen.FormCount-1 do
begin
Frm := Screen.Forms[I];
if Frm is TForm1 then
begin
TForm1(Frm).LoadValues;
Exit;
end;
end;
end;
end.
Use the form's onShow() -event.
But be aware, that the onShow() -event is called every time the form is shown, not only the first time.
In your form, you should override the DoShow method and insert your code there. Since DoShow is called everytime the form changes from invisible to visible, you should also add a boolean variable in your form and check in the DoShow override, if it is false. In that case, it is the first time DoShow is called. Then set the variable to true and do whatever you need to do the first time.
Please note that within DoShow the form is not visible yet. If you need to do something once the form is made visible, you can post a custom message from DoShow and put your code in the corresponding message handler. At the time it is executed, the form just became visible.
const
WM_APP_STARTUP = WM_USER + 1;
type
TForm1 = class(TForm)
protected
FInitialized : Boolean;
procedure DoShow; override;
procedure WMAppStartup(var Msg: TMessage); message WM_APP_STARTUP;
end;
implementation
procedure TForm1.DoShow;
begin
// Form is NOT visible but will soon be
if not FInitialized then begin
FInitialized := TRUE;
// Insert your code here
PostMessage(Handle, WM_APP_STARTUP, 0, 0);
end;
inherited DoShow;
end;
procedure TForm1.WMAppStartup(var Msg: TMessage);
begin
// The form is now visible
// Insert your code here
end;

Why my MDIChild forms not open in wsMaximized state every time?

Delphi MDIChild windowState problem
I have a delphi project with a MDI and childs. When I created a new child form, this occupies perfctly a reserved space on main form (child wsMaximized).
But when a create another child form (without closed first child), the first child "lost a wsMaximized state".
Resume: I need the child forms maximized every time, but when second child is opened, the "windowstate" another childs is changed.
First child:
procedure TfrmPrincipal.PosicionarForm(AForm: TForm);
var
Rect: TRect;
begin
GetWindowRect(Application.MainForm.ClientHandle, Rect);
InflateRect(Rect, -2 * GetSystemMetrics(SM_CXBORDER),
-2 * GetSystemMetrics(SM_CYBORDER));
OffsetRect(Rect, -Rect.Left, -Rect.Top);
AForm.BoundsRect := Rect;
end;
procedure TfrmPrincipal.actCadastroFornecedorExecute(Sender: TObject);
begin
frmCadastroFornecedor := TfrmCadastroFornecedor.Create(Application);
PosicionarForm(frmCadastroFornecedor);
frmCadastroFornecedor.Show;
svSub.Visible := False;
SV.Opened := False;
end;
Secound child:
procedure TfrmPrincipal.actCadastroProdutosExecute(Sender: TObject);
begin
frmCadastroProduto := TfrmCadastroProduto.Create(Application);
PosicionarForm(frmCadastroProduto);
frmCadastroProduto.Show;
svSub.Visible := False;
SV.Opened := False;
end;
EDIT:
I created a new project, have 3 forms. The code is very simple, but this behavior continues.
Main FORM:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
screen11: TMenuItem;
screen21: TMenuItem;
procedure screen11Click(Sender: TObject);
procedure screen21Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
unit2, unit3;
{$R *.dfm}
procedure TForm1.screen11Click(Sender: TObject);
begin
form2 := tform2.Create(Application);
form2.Show;
end;
procedure TForm1.screen21Click(Sender: TObject);
begin
form3 := tform3.Create(Application);
form3.Show;
end;
end.
Child 1:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm2 = class(TForm)
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
end.
Child 2:
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm3 = class(TForm)
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
end.
After some trial and error (once again an example of how important a complete, minimal, reproducible, example is) I was able to reproduce the error you ask about. If you had included the the .dfm files, the actual problem had been clear much sooner.
Now, to the problem that the WindowState of the forms change from wsMaximized to wsNormal after deletion of one of the childforms.
This happens when you have removed biMaximise from BorderIcons for forms Form2 and Form3
Cure: Let BorderIcons.biMaximise be selected.
I also suggest you remove PosicionarForm(AForm: TForm); from the main form. It doesn't appear to do anything useful.

"Cannot change Visible in OnShow or OnHide" error

Update:
This question escalated to a new/related question which was fortunately solved by #RemyLebeau here.
So, instead of reading below, you should go directly to Major flaw - Radio buttons are not correctly set while the form is invisible
Thanks Remy
I have two form. When I click a radiobtn, I want to hide one form and show the second one.
Hide Form1 and show Form2:
procedure TForm1.RadioButton2Click(Sender: TObject);
begin
Form2.Visible:= TRUE;
Form1.Visible:= FALSE;
end;
In Form2 I press a button to 'return' to Form1:
procedure TForm2.Button1Click(Sender: TObject);
begin
Form1.RadioButton1.Checked:= TRUE;
Form1.Visible:= TRUE; <--- this will 'magically' put the RadioButton1 back to false
end;
However, I get this error when I try to make Form1 visible:
Project Tester.exe raised exception class EInvalidOperation with
message 'Cannot change Visible in OnShow or OnHide'
Putting a breakpoint in RadioButton2Click I find out that RadioButton1 was magically rechecked during Form1.Visible:= TRUE (during TCustomForm.SetVisible more exactly).
Why is the RadioButton2 'magically' checked during SetVisible?
unit Unit1;
INTERFACE
USES
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, System.Actions, Vcl.ActnList, Vcl.StdCtrls;
TYPE
TForm1 = class(TForm)
GroupBox1: TGroupBox;
RadioButton1: TRadioButton;
RadioButton2: TRadioButton;
procedure RadioButton2Click(Sender: TObject);
procedure RadioButton1Click(Sender: TObject);
private
public
end;
VAR
Form1: TForm1;
IMPLEMENTATION {$R *.dfm}
USES Unit2;
procedure TForm1.RadioButton1Click(Sender: TObject);
begin
Caption:= '1';
end;
procedure TForm1.RadioButton2Click(Sender: TObject);
begin
Caption:= '2';
Form2.Visible:= TRUE;
Form1.Visible:= FALSE;
end;
end.
-
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
end;
VAR
Form2: TForm2;
IMPLEMENTATION {$R *.dfm}
USES Unit1;
procedure TForm2.Button1Click(Sender: TObject);
begin
Form1.RadioButton1.Checked:= TRUE;
Form1.Visible:= TRUE;
end;
end.
The workaround (not the final fix) is to do the changes to the GUI (form1) AFTER you make it visible!
Update!
The bug is related to the TabOrder property!
Details
Direct references from one form to fields of another is bad design and I highly advice you to change this. But anyway, I made two forms: first with two radiobutton, second with a button. Handlers:
{ For both radiobuttons }
procedure TForm1.RadioButtonClick(Sender: TObject);
begin
Form1.Visible := RadioButton1.Checked;
Form2.Visible := RadioButton2.Checked;
end;
{ For button }
procedure TForm2.Button1Click(Sender: TObject);
begin
Form1.RadioButton1.Checked := true;
end;
It works fine, without any remark. Delphi 10.1 Berlin.

Scoping issue with Set types

I have 3 units This is my main unit....
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
Uses Unit2;
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
Panel1.Visible := oiNone in form2.Settings.OptVars;
Panel2.Visible := oiHint in form2.Settings.OptVars;
Panel3.Visible := oiStat in form2.Settings.OptVars;
end;
end.
This is the second unit - used by the first.
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Unit3;
type
TForm2 = class(TForm)
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
CheckBox3: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Settings: TSettings;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
begin
Settings := TSettings.Create;
Checkbox1.Checked := oiNone in Settings.OptVars;
CheckBox2.Checked := oiHint in Settings.OptVars;
CheckBox3.Checked := oiStat in Settings.OptVars;
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
Settings.Free;
end;
end.
and this is the unit which is used by the second unit and contains the Set type which is used by both other units.
unit Unit3;
interface
Uses
winAPI.windows,system.classes;
Type
TOptions = Set Of (oiNone, oiHint, oiStat);
TSettings = class
private
fMyOption: TOptions;
public
Constructor Create;
Destructor Destroy;
property OptVars: TOptions read fMyOption write fMyOption;
end;
implementation
constructor TSettings.Create;
begin
fMyOption := [oiNone, oiHint, oiStat];
end;
destructor TSettings.Destroy;
begin
end;
end.
In the second unit, the items oiNone,oiHint, oiStat are accessible and within scope.
In the first unit, the items oiNone, oiHint and oiStat are not accessible and within scope although the Settings.OpVars which is a TOption data type is accessible.
I cannot think of a better way to describe my problem. If you put these into a project you should see the issue.
Update
After the latest edit it is clear that my first hunch was correct. You weren't using the unit that declares the type. The names have changed, but with the current edit your problem is that the main unit does not use Unit3.
You need to reference the unit that declares the set type, and its enumerated type. Add UnusedItemsReg to your uses clause in the third unit.
If you have done that then the only other explanation I can imagine is that you compile with scoped enums enabled. But I'm clutching at straws now.
I would definitely recommend that you declare the enumerated type as a named type in its own right. Certainly if you use scoped enums then you'll need to do that. But sooner or later you'll want to have that type available.
type
TOption = (oiNone, oiHint, oiStat);
TOptions = set of TOption;
If you do use scoped enums then you'll refer to then like this:
TOption.oiNone
You need Unit3 in the (a) uses clause of the main unit. Otherwise it cannot see type TOptions. That is a requirement of Delphi for visibility. It does not use the implicit references that you seem to be seeking.

Delphi fire frame events from datamodule

I've an application made by a main form, a frame and a datamodule.
When the main form is created I create also the frame wich contains the fields present in a table that are in the datamodule.
So the main form code is this:
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
FraDtl;
type
TfrmMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FFraDtl: TfraDetail;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
FFraDtl := TfraDetail.Create(Self);
FFraDtl.Parent := Self;
end;
end.
The code of the frame is this:
unit FraDtl;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
database, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters,
cxContainer, cxEdit, cxTextEdit, cxDBEdit, Vcl.StdCtrls, dxSkinsCore,
cxNavigator, cxDBNavigator;
type
TfraDetail = class(TFrame)
lblCognome: TLabel;
edtCognome: TcxDBTextEdit;
lblNome: TLabel;
edtNome: TcxDBTextEdit;
cxDBNavigator1: TcxDBNavigator;
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
end.
And the code of the datamodule is this:
unit database;
interface
uses
System.SysUtils, System.Classes, Data.DB, DBAccess, MSAccess, MemDS,
dxSkinsCore, cxClasses, cxLookAndFeels, dxSkinsForm;
type
TdmData = class(TDataModule)
Connection: TMSConnection;
tblAutori: TMSTable;
dsAutori: TMSDataSource;
fAut_IdAutore: TIntegerField;
fAut_Cognome: TStringField;
fAut_Nome: TStringField;
fAut_Nominativo: TStringField;
SkinController: TdxSkinController;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
procedure dsAutoriStateChange(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
dmData: TdmData;
implementation
{$R *.dfm}
procedure TdmData.DataModuleCreate(Sender: TObject);
begin
tblAutori.Open;
end;
procedure TdmData.DataModuleDestroy(Sender: TObject);
begin
tblAutori.Close;
end;
procedure TdmData.dsAutoriStateChange(Sender: TObject);
begin
// Some stuff
end;
end.
I would like to call a procedure present in the frame in the dsAutoriStateChange event is fired. There is a way to do this?
I would like to call a procedure present in the frame in the dsAutoriStateChange event is fired.
You can do this using code like this:
unit database;
....
implementation
uses FraDtl;
procedure TdmData.dsAutoriStateChange(Sender: TObject);
begin
FraDtl.SomeProcedure;
end;
Or if you want to access the actual instantiated frame you access the main unit.
unit database;
....
implementation
uses main; //circular reference allowed in implementation
procedure TdmData.dsAutoriStateChange(Sender: TObject);
begin
frmMain.FrameDetail.DoSomething(Sender);
end;
In that case you need to add an public property to frmMain exposing the frame.
TfrmMain = class(TForm)
...
public
property FrameDetail: TFraDetail read FFraDtl;
Warning
These kinds of hard coupling between data module and form are considered bad programming practice.
Loose coupling is good
Hard coupling is bad
It would be better to use an event handler for a datasource inside the mainform, or if that's not possible to add additional properties to TdmData that get updated by the main form as needed.
The event handler dsAutoriStateChange can then access these properties instead of having to poke around in the private parts of the frame.

Resources