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;
Related
I am trying to use a class to display a progress indicator.
If I declare ProgressIndicator as a variable within the calling procedure, everything works fine, and ANewForm displays as I would expect.
However, the following code produces an access violation. Can anyone help me to understand why?
unit Main;
interface
*uses
Winapi.Windows, Vcl.Forms,
System.Classes, Vcl.Controls, Vcl.StdCtrls,
Progress;
type
TProgressIndicator = class
private
public
ANewForm : TForm;
constructor Create;
end;
type
TfmMain = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
ProgressIndicator : TProgressIndicator;
end;
var
fmMain: TfmMain;
implementation
{$R *.dfm}
constructor TProgressIndicator.Create;
begin
ANewForm := TForm.Create(Application);
ANewForm.Show;
end;
procedure TfmMain.Button1Click(Sender: TObject);
begin
ProgressIndicator.Create;
end;
end.
There's a difference between ProgressIndicator.Create and TProgressIndicator.Create. Usually, you want to use the second form which says, "create a new instance of class TProgressIndicator". The first form says, "take an instance of TProgressIndicator which is stored in the variable ProgressIndicator and call its Create method". The problem is, it doesn't create that instance. In your case, ProgressIndicator is nil, because all class members are initialized to a zero-like value at the construction time, which is not a problem per-se - it still is linked to the class data so it can actually call the Create method. The method tries to create the form, which succeeds, and then tries to store it to the ANewForm field, because the in-memory address of ANewForm is Self + offset; for your code, the offset is probably 0, Self is nil, which gives a final address of (nil + 0) = 0 and the memory location 0 is located in a memory page which is prohibited from all access. That's why you get an Access Violation.
What you want is:
procedure TfmMain.Button1Click(Sender: TObject);
begin
ProgressIndicator := TProgressIndicator.Create;
end;
That will first create a new instance and then work with that.
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.
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;
When I work with TServerSocket I can use the property Data to store a pointer to a class for example for each client.
Now I will use Indy TIdCmdTCPServer and I'd like to know if there is an equivalent property.
Yes, there is - the TIdContext.Data property. In TIdCmdTCPServer events that give you a TIdCommand parameter instead of a TIdContext parameter, you can access the TIdContext object from the TIdCommand.Context property. For example:
type
TMyClass = class
// add whatever you need...
end;
procedure TForm1.IdCmdTCPServer1Connect(AContext: TIdContext);
var
MyCls: TMyClass;
begin
MyCls := TMyClass.Create;
// initialize MyCls as needed...
AContext.Data := MyCls;
end;
procedure TForm1.IdCmdTCPServer1Disconnect(AContext: TIdContext);
begin
AContext.Data.Free;
AContext.Data := nil;
end;
procedure TForm1.IdCmdTCPServer1CommandHandlerCommand(ACommand: TIdCommand);
var
MyCls: TMyClass;
begin
MyCls := TMyClass(ACommand.Context.Data);
// use MyCls as needed...
end;
Indy also has another useful feature. You can derive a custom class from TIdServerContext, add whatever you want to it, and then assign it to the server's ContextClass property before activating the server. That way, you can simply typecast any TIdContext pointer to your class type when you need to access your custom members. For example:
type
TMyContext = class(TIdServerContext)
public
// add whatever you need...
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
destructor Destroy; override;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
inherited;
//...
end;
destructor TMyContext.Destroy;
begin
//...
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
IdCmdTCPServer1.ContextsClass := TMyContext;
end;
procedure TForm1.IdCmdTCPServer1Connect(AContext: TIdContext);
var
MyCtx: TMyContext;
begin
MyCtx := TMyClass(AContext);
// initialize MyCtx as needed...
end;
procedure TForm1.IdCmdTCPServer1CommandHandlerCommand(ACommand: TIdCommand);
var
MyCtx: TMyContext;
begin
MyCtx := TMyClass(ACommand.Context);
// use MyCtx as needed...
end;
This way, you don't need to waste time and memory allocating a separate class per client, when you can use the one that the server already creates for you.
I have a main form with TPanel. I have also a Form2 with a TButton which I show in TPanel as a child. I mean TPanel of main form is parent of Form2. I use these steps to create the form2 in MainForm OnCreate method
MainFormOnCreate()
Form2 := TForm2.create(nil)
Form2.Parent := Panel1;
Form2.show;
But the problem is that when I access the button on Form2 it does nothing. For example, when I want to disable the button on Form2 I use this method
A button2 on main form with on click event
btn2OnClick();
Form2.btn.enabled := false;
But it does nothing. Some friends says it's because of child to TPanel it will get no message.
So give me a solution.
Thanks in advance
The main problem is, that you create 2 instances of TForm2.
Your .dpr file look like this
begin
Application.Initialize;
Application.CreateForm( TForm1, Form1 );
Application.CreateForm( TForm2, Form2 );
Application.Run;
end.
After you create an instance of TForm2 in TForm1.OnCreate and save this instance into global variable Form2, another instance of TForm2 is created and stored into Form2.
In the TForm1.btn5.OnClick event you address the second created, non visible TForm2.
Solution
go to Project / Options -> Formula and remove TForm2 from AutoCreate List
store the instance of TForm2 created inside of TForm1 in a private class field of TForm1
Your code should look like this
.dpr file:
begin
Application.Initialize;
Application.CreateForm( TForm1, Form1 );
Application.Run;
end.
Unit1.pas
TForm1 = class( TForm )
...
procedure FormCreate( Sender : TObject );
procedure btn2Click( Sender : TObject );
private
FForm2 : TForm2;
...
end;
procedure TForm1.FormCreate( Sender : TObject );
begin
FForm2 := TForm2.Create( Self );
FForm2.Parent := Panel1;
FForm2.Show;
end;
procedure TForm1.btn2Click( Sender : TObject );
begin
FForm2.btn.Enabled := True;
end;
Try this one
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Unit2;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
lForm: TForm;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(lForm) then
TForm2(lForm).Button1.Enabled:= False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
lForm := TForm2.Create(self);
lForm.Parent := Panel1;
lForm.Align:= alClient;
lForm.show;
end;