Add a form before Mainform - forms

I would like to program a small advertising form, which let's me easily implement it to any of my other projects.
The ad form should appear before my real project/app starts.
Some requirements have to be met:
Easy to implement to any project without any hassle - add form/unit and almost everything takes care of the rest.
My app's MainForm (Application.MainForm) shall be created during runtime AFTER my ad form has been successfully closed (or requirements have been met)
Adding only 1 unit/form should be enough to implement
My progress so far is:
I create an empty main form which is hidden (Application.MainForm)
Then I create a modalform, which is the actual Ad-Form - on the right modalresult, free my welcomescreen and proceed to the "main app"
I need to remove all auto-create forms from my project
I open a procedure in my project's source file with some parameters, including the app's main form (see source)
Unfortunately I have to add all units/forms to the project instead of just one (recursive path problem?)
That's what I have so far:
Project Source:
program MyTestProgram;
uses
Vcl.Forms,
Windows,
uMainWindow in 'uMainWindow.pas' {Form1},
uEmptyForm in '..\AdProject\uEmptyForm.pas' {AdEmptyMainForm},
uWelcomeScreen in '..\AdProject\uWelcomeScreen.pas' {WelcomeScreen}; // shouldn't be here
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.Run;
LoadAdWindow('Title of my app', uMainWindow.TForm1, uMainWindow.Form1);
end.
EmptyForm Unit:
unit uEmptyForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TAdEmptyMainForm = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
AdEmptyMainForm: TAdEmptyMainForm;
procedure LoadAdWindow (Appname: string; InstanceClass: TComponentClass; var Reference);
implementation
{$R *.dfm}
uses
uWelcomeScreen; // Has to be added to the project, otherwise it doesn't detect the unit in the same path as this unit
procedure LoadAdWindow (Appname: string; InstanceClass: TComponentClass; var Reference);
begin
Application.MainFormOnTaskbar := True;
Application.ShowMainForm := False;
Application.Title := Appname;
Application.CreateForm(TAdEmptyMainForm, AdEmptyMainForm);
with uWelcomeScreen.TWelcomeScreen.Create(Application.MainForm) do
begin
Caption := Appname;
if ShowModal <> 1337 then ExitProcess(0);
Free;
end;
Application.CreateForm(InstanceClass, Reference);
end;
end.
After the welcome screen succeeds, the application closes.
Is this even the right way to it?
Any help is appreciated!

The Application.MainForm is established by the first call to Application.CreateForm() for a TForm-derived class. Application.Run() exits immediately if Application.MainForm is not assigned.
To do what you are attempting, you should do it more like the following instead. You don't need a blank MainForm, simply create and show the ad window before creating the real MainForm. Only the project main source needs to be changed:
program MyTestProgram;
uses
Vcl.Forms,
Windows,
uMainWindow in 'uMainWindow.pas' {Form1},
uWelcomeScreen in '..\AdProject\uWelcomeScreen.pas' {WelcomeScreen};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.Title := 'Title of my app';
with TWelcomeScreen.Create(Application) do
try
Caption := Application.Title;
if ShowModal <> 1337 then
Exit;
finally
Free;
end;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.

You can easily create and show the Ad Form and after it create and show the main form, like this :
var
FAdFrm : TAdFrm;
begin
FAdFrm := TAdFrm.Create(nil);
if FAdFrm.ShowModal = mrOK then
begin
FAdFrm.Free;
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainFrm, MainFrm);
Application.Run;
end
else
FAdFrm.Free;
Be careful that the Main Form of your project is the first form that created with Application.CreateForm method

An alternative is to create the advertising form on MainForm's create event. I do this on my own projects.
procedure TMainForm.FormCreate(Sender: TObject);
begin
AdvForm := TAdvForm.Create(Self);
Try
AdvForm.ShowModal;
finally
AdvForm.Free;
end;
end;

Related

Delphi Access Violation on form show

Sorry for having to open new question but I can't find an answer anywhere.
My app is still in progress, but basically I'm trying to call another Form from my MainForm when initializing players, however I get an Access Violation error. Would you please explain to me why this could be happening?
My MainForm code:
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls, jpeg, pngimage, getPlayer_u;...
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Randomize;
InitGameSetup();
end;...
procedure TfrmMain.InitGameSetup();
begin
SetWindowProperties();
InitBackGround();
InitMainMenu();
InitGameBoard();
InitScrabbleTileRack();
InitPlayers();
// GameLoop();
end; ...
procedure TfrmMain.InitPlayers();
var
I : Integer;
sName, sSurname : string;
begin
setLength(Players, NUMBER_OF_PLAYERS);
for I := 1 to High(Players) do
begin
GetPlayer(); ---------------- problem is here
with Players[I] do
begin
Name := sName;
Surname := sSurname;
end;
end;
end;...
procedure TfrmMain.GetPlayer();
begin
frmGetPlayer.Show;
end;
My frmGetPlayer:
unit getPlayer_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TfrmGetPlayer = class(TForm)
btnSubmit: TButton;
edtName: TEdit;
edtSurname: TEdit;
procedure FormCreate(Sender: TObject);
procedure btnSubmitClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
sPlayerName, sPlayerSurname : string;
end;
var
frmGetPlayer: TfrmGetPlayer;
implementation
{$R *.dfm}
procedure TfrmGetPlayer.btnSubmitClick(Sender: TObject);
begin
sPlayerName := edtName.Text;
sPlayerSurname := edtSurname.Text;
if not ((Length(sPlayerName) >= 1) and (Length(sPlayerSurname) >= 1)) then
MessageDlg('Please enter a name and surname.', mtInformation, [mbOK], 0)
else
Self.Free;
end;
procedure TfrmGetPlayer.FormCreate(Sender: TObject);
begin
with Self do
begin
Position := poScreenCenter;
BorderStyle := bsDialog;
end;
end;
end.
My dpr:
program main_p;
uses
Forms,
main_u in 'main_u.pas' {frmMain},
getPlayer_u in 'getPlayer_u.pas' {frmGetPlayer};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TfrmMain, frmMain);
Application.Run;
end.
The error:
Only your MainForm object is created automatically at program startup. Inside its OnCreate event, your Player Form object hasn't been created yet, so the frmGetPlayer variable is not pointing at a valid object.
frmGetPlayer is a global variable, so it is initially nil. The error message is telling you that you are accessing invalid memory near address 0, which is almost always an indication of accessing a member of a class via a nil pointer.
So, you can't call frmGetPlayer.Show() until after you have created the Player Form object and assigned frmGetPlayer to point at it. Which the code you showed is not doing.

Passing a Variable to different forms / units

I want to use a variable from my main form in another form, each form has its own unit.
I want to use iUser from Login_u in Result_u
I found an article where they said i should put the variable in public declaration and under implementation 'uses and then the unit that wants to access the variable'. Also in the unit that wants to access that variable under implementation uses and then the unit name from where it wants to get the variable
unit Login_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, jpeg, ExtCtrls, StdCtrls;
type
TfrmLogin = class(TForm)
btnLogin: TButton;
cbxUser: TComboBox;
procedure btnLoginClick(Sender: TObject);
private
{ Private declarations }
public
iUser:Integer;
{ Public declarations }
end;
var
frmLogin: TfrmLogin;
implementation
uses Result_u;
{$R *.dfm}
procedure TfrmLogin.btnLoginClick(Sender: TObject);
begin
iUser:= cbxUser.ItemIndex;
end;
end;
end.
In my result unit i get the error undeclared identififier, I used the activate procedure and a show message just as a test
unit Result_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, DBGrids, jpeg, ExtCtrls;
type
TfrmResult = class(TForm)
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmResult: TfrmUitslae;
implementation
uses Login_u;
{$R *.dfm}
procedure TfrmUitslae.FormActivate(Sender: TObject);
begin
ShowMessage(iUser);
end;
end.
I have read a few articles about this but I keep getting lost, I'm a highschool student so it doesnt need to be complex code.
Usually you should not use global variables. The IDE adds global form variables when you create the forms and automatically creates them at startup.
Try to get in the habit to delete these variables and avoid creating them at startup. Normally only the main menu is sufficient at startup.
A login form should be a modal dialog. Assign the btnLogin button a ModalResult value of mrOk. This means that the login form will be closed with this result when the button is pressed. Note that the uses Result_u; declaration in unit TfrmLogin must be removed.
Here is a demonstration how to create the login form and how to obtain the iUser value through an instance of TFrmLogin:
unit Result_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, DBGrids, jpeg, ExtCtrls;
type
TfrmResult = class(TForm)
procedure TestLogin;
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
uses Login_u;
{$R *.dfm}
procedure TfrmResult.TestLogin;
var
frmLogin: TFrmLogin;
begin
frmLogin := TFrmLogin.Create(Nil);
try
if frmLogin.ShowModal = mrOk then
ShowMessage('User login index is:'+IntToStr(frmLogin.iUser));
finally
frmLogin.Free;
end;
end;
end.
The easiest way to use that variable as you intend it is to just move its declaration out of the form class, so it will be global, always available (without needing to create and address an instance of the form).
unit Login_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, jpeg, ExtCtrls, StdCtrls;
type
TfrmLogin = class(TForm)
btnLogin: TButton;
cbxUser: TComboBox;
procedure btnLoginClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmLogin: TfrmLogin;
iUser:Integer;
implementation
uses Result_u;
{$R *.dfm}
procedure TfrmLogin.btnLoginClick(Sender: TObject);
begin
iUser:= cbxUser.ItemIndex;
end;
end;
end.
Now you can use it inside Result_u as you have already tried.
Note: You declare a variable within the public section of a form class (as you did in your code) when that variable needs to hold different values at every instance of the form.

Passing variable from project source to form

I am putting some logic into the project source before form creation. If some conditions are met, I proceed with form creation. The logic is generating data that I need to pass to the form, let'say some variable. I declared these variables inside the public section of the form, but I can't find a way to pass these values, since the variables need the form to be created to exist.
Is there a way? I am using Delphi 2007.
I suggest to set these variables once the condition is met.
Run your logic
Check the condition
Create form
Assign variables on the form
In the project source:
var
MyVariable1 : integer;
MyVariable2 : integer;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
//some logic to assign variables
//...
//create form and set variables only if condition is met, example:
if(MyVariable1 + MyVariable2 > 10) then
begin
Application.CreateForm(TForm1, Form1);
Form1.MyVariable1 := MyVariable1;
Form1.MyVariable2 := MyVariable2;
end;
Application.Run;
end.
In the form source:
TForm1 = class(TForm)
private
{ Private declarations }
public
MyVariable1 : integer;
MyVariable2 : integer;
{ Public declarations }
end;
How you're creating the form?
You can do something like this:
f := TMyForm.Create(Application)
f.MyProperty := 10;
f.Show;
Regards.
You can declare a global variable in form unit and use it in the form unit
you can set global var every where you want
TForm9 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
MyLocalVar : Int64;
end;
var
Form9: TForm9;
MyGlobalVar : Int64;
implementation
{$R *.dfm}
procedure TForm9.FormCreate(Sender: TObject);
begin
MyLocalVar := MyGlobalVar;
end;

Delphi xe3 Mainform hide

I have been trying to start my application with hiden main form, but no luck. It
compiles and everything, but when I run it I get runtime error. When I use timer and set it to 1 millisecond and then call Application.MainForm.Hide it hides but it flashes i dont want that to happen
program Project1;
uses
FMX.Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.MainForm.Visible := false;
Form1.Visible:=false;
Application.Run;
end.
In a FireMonkey application the Auto-created forms (are created) and the MainForm property is assigned in the Application.Run method. So the access violation is caused because the MainForm property and form1 variable is nil.
In order to access such properties you must execute the RealCreateForms method first
begin
Application.Initialize;
Application.CreateForm(TForm2, Form1);
Application.RealCreateForms;
//Application.MainForm.Left:=-Application.MainForm.Width;
Application.MainForm.Visible:=False;
Application.Run;
end.
Much easier method - override CanShow:
type
TfrmMain = class(TForm)
public
function CanShow: Boolean; override;
end;
...
function TfrmMain.CanShow: Boolean;
begin
Result := False; // Or return True when it's OK to show
end;

Visual inheritance a form from a base form in another package without sources in Delphi XE2

Having a situation: two packages: "Base" and "Descendant" and an application "Example".
The Base package and the Example application may be in one project group, but the Descendant package has to be in other project group, without any source code of Base and Example.
The aim of such manipulation is to hide Base and Application sources from workers who will work with Descendant package.
Base package contains a form: TFormBase with some components and some code. I build it and get some binary files: bpl, dcp, etc...
type
TFormBase = class(TForm)
Panel1: TPanel;
BOk: TButton;
BCancel: TButton;
procedure BOkClick(Sender: TObject);
procedure BCancelClick(Sender: TObject);
private
protected
function GetOkButtonCaption: string; virtual;
function GetCancelButtonCaption: string; virtual;
public
end;
implementation
{$R *.dfm}
procedure TFormBase.BCancelClick(Sender: TObject);
begin
ShowMessage('"' + GetCancelButtonCaption + '" button has been pressed');
end;
procedure TFormBase.BOkClick(Sender: TObject);
begin
ShowMessage('"' + GetOkButtonCaption + '" button has been pressed');
end;
function TFormBase.GetCancelButtonCaption: string;
begin
Result := 'Cancel';
end;
function TFormBase.GetOkButtonCaption: string;
begin
Result := 'Ok';
end;
Descendant package contains TFormDescendant = class(TFormBase)
type
TFormDescendant = class(TFormBase)
private
protected
function GetOkButtonCaption: string; override;
function GetCancelButtonCaption: string; override;
public
end;
implementation
{$R *.dfm}
function TFormDescendant.GetCancelButtonCaption: string;
begin
Result := 'Descendant Cancel';
end;
function TFormDescendant.GetOkButtonCaption: string;
begin
Result := 'Descendant Ok';
end;
And code of Descendant.dfm:
inherited FormDescendant: TFormDescendant
Caption = 'FormDescendant'
end
Descendant.dpr:
requires
rtl,
vcl,
Base;
contains
Descendant in 'Descendant.pas' {FormDescendant};
When creating FormDescendant it should look like FormBase because it is simply inherited from it. And we can add some other components on this FormDescendant saving FormBase look.
But when we try to open FormDescendant in Delphi IDE, it crashes with "Error creating form: Ancestor for 'TFormBase' not found."
And that's right: Base.bpl contains only binary code and Delphi doesn't know how TBaseForm looks in design-time.
What should I do to open FormDescendant in Delphi?
I've read How do I use or resolve issues with visual form inheritance in Delphi? and Register custom form so I can inherit from it from multiple projects, without copying the form to the Object Repository folder
But those advices didn't help.
Is there a way to open FormDescendant in design-time without sources of TFormBase?
Here're the projects of the example for experiments: http://yadi.sk/d/IHT9I4pm1iSOn
You can provide a stub unit with (some of the) implementation code stripped, only the .dfm and interface has to be identical. This is what Allen Bauer did for his Opening Doors article showing how to implement dockable IDE forms.
Your developers will then need to first open the stub form unit and then they will be able to open the descendant form.