I'm trying to load a form when my main form starts. And I do it using ShowModal. Also that form is not an auto create form so I have to create it first with application.CreateForm.
My problem is when I try to load the form in FormCreate event, it load the form twice and when I close the form my whole app closes.
Here is my code:
procedure Tfrm_main.FormCreate(Sender: TObject);
var
username, password : string;
begin
username := ini.ReadString('user','username','');
if username = '' then
begin
application.CreateForm(Tfrm_user,frm_user);
frm_user.ShowModal;
end;
end;
How can I fix this problem? Thanks.
The code in your question is called from the call to Application.CreateForm that creates the main form. Then you call Application.CreateForm again, recursively, and that results in the Tfrm_user instance becoming the main form.
It's well known that the first form created by Application.CreateForm becomes the main form. Here, you call Application.CreateForm to create the main form. But before the code of Application.CreateForm gets to determine what the VCL considers to be the main form, the recursive call to Application.CreateForm executes. All the way to the end, and in doing so determines the main form to be the secondary form made with the recursive call.
You then show the secondary form modally. Later you call Application.Run which shows the VCL main form, your secondary form. Again. And then you close it. Which closes the program, because that's what happen when you close the VCL main form.
My advice is to call Application.CreateForm exactly once in the life of your program. So, in the OnCreate handler create the other form like this:
frm_user := Tfrm_user.Create(Application);
Or perhaps let the main form be the owner. And certainly consider not using the global variable frm_user. I'd remove that.
Or another option would be to show the secondary form modally before you make the call to Application.CreateForm.
You're getting bitten by an order-of-operations issue.
If you look at the code to TApplication.CreateForm, you'll see that things happen in this order:
Create a new form of the appropriate type.
If FMainForm = nil, assign this new form to it.
Later, TApplication.Run calls FMainForm.Show;
But when the program is setting up your principal form:
It first creates it, which calls the OnFormCreate event handler. Then the event handler calls TApplication.CreateForm, (before the first one has returned) which creates the new form, sees that FMainForm is not assigned and assigns this form to it, then returns.
Your FormCreate then shows this form as a modal, then returns.
Things unwind a little, and we're back to the original TApplication.CreateForm, which goes on to the next part, sees FMainForm already assigned, and does not assign it.
Then it returns, and TApplication.Run gets called, which shows the main form... the wrong one.
IF you want to create the new form, call the constructor instead: frm_user := Tfrm_user.Create(Application); Really, TApplication.CreateForm should only be used the one time, to set up the main form. It's kind of a hack that can make trouble for you if you don't know exactly how it works.
Related
I'm relatively new to Delphi, so please pardon my ignorance.
As an exercise, I am writing a little MasterMind game. The idea is that, when the user derives the correct number, a custom method (Congrats) executes.
One of the options in this method is to play again. The obvious step following this is that the form must be reset to its default ("start up") state. I have created a type method for that (Resetform, declared as a method in public, as it needs to access the controls). I can't call that from Congrats, but I noticed that I can call it from other event handlers.
Is it possible to do this, and if so, how do I go about it?
Works from event handler, but not from custom procedure.
The method Resetform is a method of your form, which is why you can call it from event handler of your form, they reside in the same object.
To call your Resetform from outside the context of your form, you need a reference to your form, otherwise the code won't know which instance of your form it needs to call Resetform on.
Now, assuming you call Congrats from your form, you could add a parameter and call it like this:
Congrats(Self)
And your procedure could be implemented as
procedure Congrats(AForm : TMyForm); // "TMyForm" being the class of your form
begin
...
AForm.Resetform;
end;
But then, you could as well make Congrats a method of your form instead.
If Congrats is not called from your form, then you will need to find another way to pass your form's reference to the function, or use another approach.
I am new to Delphi and my question may be very basic.
I created a Form in a Delphi procedure. Until now, I was using ShowModal() and then freeing all the objects that I want to avoid leaking after closing the Form.
Now, I would like to show the Form modeless, but I don't know how I can free the objects inside the OnClose event.
Does anybody know a solution for it?
Simply set the Action parameter to caFree:
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
Per the documentation:
The value of the Action parameter determines if the form actually closes. These are the possible values of Action:
caNone
The form is not allowed to close, so nothing happens.
caHide
The form is not closed, but just hidden. Your application can still access a hidden form.
caFree
The form is closed and all allocated memory for the form is freed.
caMinimize
The form is minimized, rather than closed. This is the default action for MDI child forms.
Setting Action to caFree will cause the Form to call Release() on itself after the OnClose handler has exited:
Destroys the form and frees its associated memory.
Use Release to destroy the form and free its associated memory.
Release does not destroy the form until all event handlers of the form and event handlers of components on the form have finished executing. Release also guarantees that all messages in the form's event queue are processed before the form is released. Any event handlers for the form or its children should use Release instead of Free (Delphi) or delete (C++). Failing to do so can cause a memory access error.
Note: Release returns immediately to the caller. It does not wait for the form to be freed before returning.
Release() posts a delayed CM_RELEASE message to the Form window. Once execution flow returns to the main message loop and the message is dispatched, the Form will free itself from memory.
If your TForm object owns other objects, they will be freed automatically when the TForm is freed.
You can also
for each dynamically created object do
Object.Free;
Current Delphi versions (Since Xe) documentation recommends to use new approach
Object.DisposeOf;
This new approach works optimized in multi-device application (different operational system)
The only way I see is to add flag for this, but is this the best way?
When the form is destroyed and I check if(Assigned(form2)) the result is true? Why?
What is the way to do this?
You can use Form1.Showing to see if a form is closed or not.
Just closing a form does not free it unless you set Action := caFree in OnClose event. Default is caHide.
Wow, a blast from the past :)
The way that Assigned() works, is that it basically does nil check on the pointer. If you destroy form2, there will still be a memory address that form2 points to.
I has been a very long time since I've done any Delphi, but from memory, you need to manually set the form2 var to nil when it is destroyed. If you have a central place (eg. a form broker?) where you create & destroy forms, this should be quite easy.
If you use Form1.Free or Form1.Destroy, Delphi will destroy the object but wont set the object reference to nil. So instead use FreeAndNil.
For more information, check Andreas Rejbrand answer in this link
Faced with the same issue when doing some routine on closing application. In this case all forms are destroyed behind the stage but pointers are not set to nil. This code helphs me:
procedure TMyForm.FormDestroy(Sender: TObject);
begin
MyForm:=nil;
end;
So pointer becomes nil and I can check it with Assigned or compare to nil.
Just as a tip, the correct way to do it in some special cases is to create a timer that do the nil assign to the variable.
I will explain it (somehow it is complex), if you create your form within your own code MyForm:=TMyForm.Create and you have a MyFrom.Close it is very easy, just add a MyForm:=nil or also better MyForm.FreeAndNil... but sometimes the reference is not anyware.
Sample: You create inside a procedure, on a loop a lot of copies of the same form (or just one), let the form open and end that procedure, now the reference to the opened form is nowhere, so you can not assign nil or do a freeandnil, etc., in a normal way.
For that cases, the trick is to use a timer (of just one milisecond) that do it, that timer needs the reference, so you must store on a global like the reference to Self, all that can be done on the on close event.
The easiest way to do the free (when no reference anywhere) is to create a TObjectList on the main form, so it will hold all form references that needs to be free, and define a timer (one milisecond) that will go through that list doing the freeandnil; then on the onlcose you add Self to that list and enable that timer.
Now the other part, you have a normal form that is auto created on start, but you need to set it to nil and re-create it on your own code.
That case has a global that point to that form, so you only need to free and nil it, but NOT (i say it loud) on any part inside the own form code, you must do it OUT (i say if loud) of the form code.
Some times you will need to free the form, when user close it, and it is not shown in modal, this case is complex, but again the same trick is valid, on the onclose event you enable a timer (that is out of that form, normally on main form) adn that timer will free and nil. That timer interval can be set as just one milisecond, it will not be run until form has totally closed (please have in mind not using Application.ProcessMessages, that is normally a really bad idea).
If you set Self to nil, free or whatever inside the own form, you can corrupt your application memory (doing that is totally unsafe, not to mention it can eat ram).
The only way to free a form (and nil its reference), a form that is not shown as modal and is the user who close it, is to program a trigger that do that after the form is totally closed.
I know about setting action to do the free, but to set it to nil there is no other safe way.
Must say: If you use timers on your main form, run a Enabled:=False on all of them on the Onclose event... otherwise weird things may occur (not allways, but sometimes... race conditions about destroying application and running code on that timers), and of couse if some one was enabled act correctly to terminate it correctly or abort it, etc.
Your question is one of the complex things to do... free and nil a form that is closed not by code, but by user action.
For all the rest: Think like if the application has at the same time a lot of forms opened and all can interact with the user at the same time (anyone is modal), and you have code that references some of them from the others... you need to know f user has closed any form to avoid accesing that form from code. This is not trivial to be done unless you use timers.
If you have a 'central' form (like an MDI application) you can put that timer on the main MDI form, so any child form that is closed can be freed and nil, the trick is again a timer on that main form.
Only and only if you are sure you can free and nil all non visible forms, you can have a timer on the main form that goes through all forms and if Visible is false, then call FreeAndNil, i consider this way prone to errors, since if you add on a future a form that must not be freed but can stay hidden... this code will not be valid.
Allways remember that if is the user the onw who closes the form that must be freed and nil, there is no way on code to detect and act, no event is launched (after the form is totally closed) and before the form is totally closed you must not even try to free it or nil its reference, weird things can occur (more prone to that if motherboard has more than one socket, also more if your app uses threads, etc).
So, for threaded apps (and also not threaded) i use another method that works great and do not need timers, but need double checking prior to each ThatForm.*, the trick is to define a Form bolean public variable like as PleaseFreeAndNilMe on the public section on the form, then on the onclose (as last line) set it to True and on the OnCreate set it as False.
That way you will know if that form had been closed or only hidden (to hide a form never call close, just call hide).
So coded will look like (you can use this as a warper, instead of defining forms as TForm define them as TMyform, or also better, use a hack like type TForm=class(Forms.TForm) instead of TMyForm=class(TForm) just to have that variable added to all forms):
TMyForm=class(TForm)
...
public
PleaseFreeAndNilMe:=Boolean;
...
procedure TMyForm.FormCreate(Sender: TObject);
begin
PleaseFreeAndNilMe:=False;
...
end;
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
...
PleaseFreeAndNilMe:=True;
end;
If you preffer hacked version:
TForm=class(Froms.TForm)
public
PleaseFreeAndNilMe:=Boolean;
end;
procedure TForm.FormCreate(Sender:TObject);
begin
inherited Create(Sender);
PleaseFreeAndNilMe:=False;
end;
procedure TForm.FormClose(Sender:TObject;var Action:TCloseAction);
begin
PleaseFreeAndNilMe:=True;
inherited FormClose(Sender,Action);
end;
But as i said, prior to access any member (or just where you do the nil compare) just call a 'global' function passign the reference (no matter if it was nil or not), coded as:
function IsNilTheForm(var TheForm: TMyForm);
begin
if nil=TheForm
then begin // The form was freed and nil
IsNilTheForm:=True; // Return value
end
else begin // The form refence is not nil, but we do not know is it has been freed or not
try
if TheForm.PleaseFreeAndNilMe
then begin // The form is not freed but wants to
try
TheForm.Free;
except
end;
try
TheForm:=Nil;
except
end;
IsNilTheForm:=True; // Return value
end
else begin // The form is not nil, not freed and do not want to be freed
IsNilTheForm:=False; // Return value
end;
except // The form was freed but not set to nil
TheForm:=Nil; // Set it to nil since it had beed freed
IsNilTheForm:=True; // Return value
end;
end;
end;
So where you do if nil=MyForm then ... you can now do if (IsNilTheForm(MyForm)) then ....
That is it.
It is better the timer solution, since form is freed as soon as possible (less ram used), with the PleaseFreeAndNilMe trick the form is not freed until IsNilTheForm is called (if you do not free it any where else).
That IsNilTheForm is so complex because it is considering all states (for a multi socket motherboard and threaded apps) and letting the code free / nil it anywhere else.
Of course, that function must be called on main thread and in atomic exclusion.
Free a Form and Nil its pointer is not a trivial thing, most when user can close it at any time (since no code out of the form is fired).
The big problem is: When a user closes a form there is no way to have a event handler that is triggered out of that form and after the form ends all things it is doing.
Imaigne now that the coder has put a lot of Application.ProcessMessages; every where on the app, also on that form, etc... and has not taken the care for race conditions... try to free and nil such a form after the user asks it to be closed... this is a nightmare, but can be solved with the hacked version of TForm that has a variable that tells that the form has not been freed but wants it.
Now imagine you use hacked TForm and want a normal TForm, just define it as ...= class(Forms.TForm), that way it will now have that extra variable., so calling IsNilTheForm will act as comparing to nil.
Hope this helps VCL coders to FIX such things, like raising an event when an object is destroyed, freed, niled, hide, etc... out of the code of that object, like on main form, etc. That would make live easier... or just fix it... Close and Free implies set to Nil all refences that point to it.
There is another thing that can be done (but i try to allways avoid it): Have multiple variables that point to the exact same form (not to copies ot it), that is prone to a lot of errors, you free one and need to nil all of them, etc... The code i show is also compatible with that.
I know the code is comples... but Free and Nil a form is more complex than my code.
I have functions I want to perform after my app has finished initialising and the main form has been created. I did have the code (call it ProcedureX) in the forms OnShow event, but I have just noticed that it is being called twice, because OnShow is firing twice. It fires when the main program DPR calls:
Application.CreateForm(TMainForm, MainForm) ;
as I would expect. But after that, when I read stuff from an INI file that includes the forms on-screen position, I have a call:
MainForm.position := poScreenCenter ;
This, it would appear fires the OnShow event again.
Where can I put my call to ProcedureX, which must only be called once, and which needs the main form to be created before it can execute?
If your code only needs to run once per form creation (or per application and the form is only created once per application run), put the code in the form's OnCreate handler. It is the natural place for it to go.
Nowadays (since D3 I think) the OnCreate fires at the end of the construction process in the AfterConstruction method. Only if you were to set OldCreateOrder to True (and it is False by default), might you get in trouble as that makes the OnCreate fire at the end of the Create constructor.
The normal order of execution for a Form is :
AfterConstruction: when the form and it components are fully created with all their properties.
OnShow: whenever the Form is ready to show (and, yes, any change causing a CM_SHOWINGCHANGED can trigger an OnShow)
Activate: whenever the Form takes the Focus
So, depending on what you need in ProcedureX, AfterConstruction might be enough, and is executed only once; just override it and add ProcedureX after inherited. It'll be after OnCreate.
If it is not the case, you can post a custom message to your Form from AfterConstruction, it will be queued and will reach your custom handler after the other messages have been handled.
In both cases, you would not need a extra boolean Field.
#Sertac,
There's really no need for the FRUNOnce field; simply do OnShow=NIL as the first line of your FormShow method.
FYI, The "run once" idiom -- setting the event handler field to NIL in the first line of the event handler -- is also terribly useful for getting some code up-and-running once a form has been completely initialized. Put your code in a FormActivate method and, as the first line of the method, set OnActivate=NIL.
You can test and set a flag once you call the procedure for the first time. Like so:
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
FRunOnce: Boolean;
public
[...]
[...]
procedure TForm1.FormShow(Sender: TObject);
begin
if not FRunOnce then begin
FRunOnce := True;
ProcedureX;
end;
end;
You can add a procedure in your DPR file, after Application.CreateForm.
Put all code you need to initialize in that procedure.
Works best when you have multiple forms in your app.
Also if the initialization takes a lot, it let's the program to display the forms on the screen so the user will know that the app is loading.
Example:
PROGRAM MyProgram;
begin
Application.Initialize;
Application.CreateForm(TMyForm, MyForm);
MyForm.Show;
LateInitialize; <----------- here
Application.Run;
end.
I'm going to propose a bit different approach to this answer by Server Overflow. We will achieve almost exactly same effect, but without any edit inside the DPR file (main project source file). We will get there by using a class helper in the unit of our main form:
type
{ TAppHelper }
TAppHelper
= Class helper for TApplication
Public Procedure Run;
End;
Procedure TAppHelper.Run;
begin
Unit1.MainForm.PreRun;
inherited Run;
end;
Notice, the Unit1.MainForm.PreRun is some method in your main form, with only one caveat: if your main form is called "MainForm", then you need to prefix it with your unit's name inside the helper's method, because the TApplication class already has a member called MainForm. Incidentally, if you do leave out the prefix, this might still work, given that your Unit1.MainForm is indeed application's main form as well.
The reason why this works, is because the Unit1 is on the uses list of the DPR project, and as long as the TAppHelper is defined in the interface section (not in the implementation section), it will get loaded and by the time the Application.Run method is called in the DPR file, this will already be the helper version of it.
The beauty of this is, that it will run exactly one time, and exactly after all the forms are already created, after all their constructors have already been executed. And the fact that we're effectively customizing the Application.Run call in the DPR file, without editting the DPR file, is kind of ingenious. Again, class helpers in delphi/lazarus !
I will share one more neat trick, first take a look:
Procedure TAppHelper.Run;
begin
TTask.Run(
procedure
begin
sleep(10);
TThread.Synchronize(nil, procedure begin Unit1.MainForm.PreRun; end);
end
);
inherited Run;
end;
This is a trick I use whenever I want the code to execute with a small delay. Why? Because if your code runs before the inherited Run method, it might (depending what happens inside of that code) hang the UI momentarily, but just long enough for the form to flicker and appear not responsive during its startup. Also, we can't simply put the code behind the inherited Run method, because that won't get executed until the application gets terminated. So instead I use TTask from the System.Threading unit. The sleep(10) is probably an overkill, sleep(1) would most likely do the job, possibly even no sleep at all would work, but I do some complex initialization there, so I keep the delay generous. Bonus: if you don't update UI from your PreRun custom method, then you don't even need TThread.Synchronize wrapper, and it becomes even simpler. In case of FPC/Lazarus you can achieve the same by using TApplication.QueueAsyncCall() instead of TTask class.
I really think it's a neat trick, because I can code it entirely outside of the DPR file, in the unit of the form which defines the PreRun method, and it's guaranteed after ALL Forms are already created, not just the one where I implement my PreRun method. Also, if the class helper is in the unit of the form, instead elsewhere, then the PreRun doesn't even need to be public, it will work with protected or even private method as well! This is great for keeping this little logic away from any other part of the code.
#Sertec,
Your code won't work either if you want it to run for every unhide event (you haven't put in any code to reset the frunonce field).
So your method would need to reset frunonce field, and mine would need to set OnShow=FormShow. Same difference, except that you need an additional field.
I am just wondering if I'm doign something that might be bad, although it seems a very practical solution to me...
I have two forms which the user will have to walk through. The user clicks on a button and form1 pops up. The user presses OK and the second one pops up. The user click OK again and the screens are gone. Or the user clicks on Retry and the screen goes back to the first one. The two screens are completely different sizes with different information.
So I came up with this code:
Form1 := TForm1.Create(SharedData);
Form2 := TForm2.Create(SharedData);
repeat
ModalResult := Form1.ShowModal;
if (ModalResult = mrOK) then ModalResult := Form2.ShowModal;
until (ModalResult <> mrRetry);
Form1.Release;
Form2.Release;
I've tested this code and it seems to work like a charm. In this code, SharedData is an object that contains data that's manipulated by both forms. I create this object before the two forms are created and when ModalResult==mrOK I just write the data back to the database.
Problem is, while I think this is a clean solution to handle flipping between two forms, I can't remember ever seeing something like this construction before. Of course, I'm a Genius. (At least, me Ego tells me I am.) But would there be something against using this piece of code or is it just fine?
If the code does what it's supposed to, I think it's fine. I've not seen anything quite like it before either, but it's pretty obvious what it's doing... which in my book is far better than just blindly following other people's work. :-)
I don't see anything wrong with the code. I do question, however, the passing of SharedData to the constructor of both forms. Unless you've overridden the constructor (using reintroduce as well), the argument to TForm.Create() accepts an owner; that owner is responsible for freeing the objects it owns. Since you're freeing them yourself, there's no need to assign an owner; just pass nil to the call to Create() to avoid the overhead of saving the reference, accessing the list of owned objects when one is freed to remove the reference, etc.
Also, Release is designed to be called from within the event handler of the control itself, such as in a button click event. It makes sure that all pending messages are handled before the control is actually freed, in order to avoid AVs. Using it the way you are again adds unnecessary overhead, as you're not using them inside an event handler. You can safely just use Form1.Free; instead.
To clarify use of Release, it's for use in code of the Form itself. For instance, if you have a button on the form, and you want that button's click to cause the form to be freed, you use Release:
procedure TForm1.Button1Click(Sender: TObject);
begin
Self.Release;
Close;
end;
This allows the button click to free the form, but makes sure that the subsequent call to Close runs first. If you use Free instead of Release above, you could end up calling Close on a non-existent Form1. (Chances are it would still be OK, because of the silliness of the code above; the form would still be resident in memory. Still a bad idea, though.)