rng
asked on
Restart button - Close the Form and re-create a new instance
Hello,
My application has only ONE Form which will be "CreateForm" once the application start.
Now I added a "Restart" button onto the Form. When users click on this button, I want the Form to "Close", and then a new instance of the same Form will be created (without terminating the application). How can I write it ??
Thanks.
rng
My application has only ONE Form which will be "CreateForm" once the application start.
Now I added a "Restart" button onto the Form. When users click on this button, I want the Form to "Close", and then a new instance of the same Form will be created (without terminating the application). How can I write it ??
Thanks.
rng
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
What is the difference between two approachs?
Could you give some remarks to the code?
Thanks.
rng
Could you give some remarks to the code?
Thanks.
rng
1). The first approach is more detailed and uses the variable Self more often .... Except that it demonstrates that after executiong the new instance of Form1 appears at 32 bits below of the previous one, because if you don't change the Top property it will be invisible to you that there is a new instance;
2). The second approach is the simplest and the right way.
2). The second approach is the simplest and the right way.
ASKER
esoftbg:
I just tried your code, but it doesn't work. Some components warned that only one instance is allowed at a time. Any other way to do it?
Thanks.
rng
I just tried your code, but it doesn't work. Some components warned that only one instance is allowed at a time. Any other way to do it?
Thanks.
rng
The method: Release kills the previous instance
The method: Application.CreateForm(TFo rm1, Self); creates a new one ....
???? There is no two instance at the same time ????
The method: Application.CreateForm(TFo
???? There is no two instance at the same time ????
ASKER
I don't know why, but seomtimes it popup the error message, sometimes it doesn't.
Any way to get around this problem?
Thanks.
rng
Any way to get around this problem?
Thanks.
rng
If you can send me your project via e-mail, I'll try to fix it, but if not it is difficult to divine ....
ASKER
As the application contains some third party components, I think I can't send the code to you.
I would like to ask that does the "Release" function completely kill the instance ?
Now the problem is that when the new instance is created, the "TfrmMain.FormCreate" event handler will be run and the component will be initialized again, thus it warned only one instance of the component can be active at a time.
Any idea?
rng
I would like to ask that does the "Release" function completely kill the instance ?
Now the problem is that when the new instance is created, the "TfrmMain.FormCreate" event handler will be run and the component will be initialized again, thus it warned only one instance of the component can be active at a time.
Any idea?
rng
FROM DELPHI 7 ENTERPRISE HELP:
Release method:
Destroys the form and frees its associated memory.
Description:
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.
//........................ .......... .......... .......... .......... .......... .......... .......... .......... .......... .......... .......... .......... .......... ..
May be you need to delay a little bit after Release the current and before Create a new instance:
procedure TForm1.SpeedButton1Click(S ender: TObject);
begin
Release;
Sleep(100); // or Sleep(400);
Application.CreateForm(TFo rm1, Self);
Show;
end;
Release method:
Destroys the form and frees its associated memory.
Description:
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.
//........................
May be you need to delay a little bit after Release the current and before Create a new instance:
procedure TForm1.SpeedButton1Click(S
begin
Release;
Sleep(100); // or Sleep(400);
Application.CreateForm(TFo
Show;
end;
This is the best solution:
procedure TForm1.SpeedButton1Click(S ender: TObject);
var
F: TForm1;
begin
Release;
Application.CreateForm(TFo rm1, F);
F.Show;
end;
procedure TForm1.SpeedButton1Click(S
var
F: TForm1;
begin
Release;
Application.CreateForm(TFo
F.Show;
end;
ASKER
Unforunately, this doesn't solve the problem. I think the "Release" function doesn't work as expected...
Thanks for your help.
rng
Thanks for your help.
rng
Note: Release returns immediately to the caller. It does not wait for the form to be freed before returning.
In this case the only way is to use another variable for the same class, this way there is not a conflict between not just freed old instance and the new one. It must work properly !
In this case the only way is to use another variable for the same class, this way there is not a conflict between not just freed old instance and the new one. It must work properly !
ASKER
Are you talking about the last sample code ? I tried it already, but the same problem arise.
rng
rng
Yes, I am talking about the sample code wich is posted at the same time (it is 4:00 EET) with your comment....
That you talking about is a very strance case: I am using a different variable ! Where is the conflict ????
Sorry, in case you can not send your project, I can not help you :-(
You may be suse the Release procedure works properly, I am using it since years.... Just your case is a different one .... May be the reasont is the other one, but I can not say nothing without to take a look at the code.
That you talking about is a very strance case: I am using a different variable ! Where is the conflict ????
Sorry, in case you can not send your project, I can not help you :-(
You may be suse the Release procedure works properly, I am using it since years.... Just your case is a different one .... May be the reasont is the other one, but I can not say nothing without to take a look at the code.
Something more: all the examples I posted here are working once for me. I tested them before post .... So your problem is any where in your code .... Sorry.
ASKER
Yes, I understand that your code should work, but just for some reason, it doesn't work for me. So I would like to know can you think of another approach instead of "Release & Show" which can do the same thing ??
Thanks.
rng
Thanks.
rng
O.k., try 2 variants:
1). Without Release:
procedure TForm1.SpeedButton1Click(S ender: TObject);
var
F: TForm1;
begin
Hide; // Hides the current Form without destroying it;
Application.CreateForm(TFo rm1, F); // Creates a new independent Form;
F.Show;
end;
2). First create a new Form, then destroy the current one:
procedure TForm1.SpeedButton1Click(S ender: TObject);
var
F: TForm1;
begin
Application.CreateForm(TFo rm1, F);
Release;
F.Show;
end;
1). Without Release:
procedure TForm1.SpeedButton1Click(S
var
F: TForm1;
begin
Hide; // Hides the current Form without destroying it;
Application.CreateForm(TFo
F.Show;
end;
2). First create a new Form, then destroy the current one:
procedure TForm1.SpeedButton1Click(S
var
F: TForm1;
begin
Application.CreateForm(TFo
Release;
F.Show;
end;
I will go to the pharmacie and will be back in about 30 minutes ....
ASKER
It works now!!
I need to deinitialize those 3rd party components before calling the Release function.
Now both procedures are working, which one should I pick ? Also, I can't notify the effect of "Top := T + 32;", what is the usage of it ?? Thanks so much for your help!
-------------------------- ---------- ------
procedure TForm1.SpeedButton1Click(S ender: TObject);
var
T: Integer;
begin
T := Top;
Self.Release;
Application.CreateForm(TFo rm1, Self);
Top := T + 32;
Self.Show;
end;
-------------------------- ---------- -------
procedure TForm1.SpeedButton1Click(S ender: TObject);
var
F: TForm1;
begin
Release;
Application.CreateForm(TFo rm1, F);
F.Show;
end;
I need to deinitialize those 3rd party components before calling the Release function.
Now both procedures are working, which one should I pick ? Also, I can't notify the effect of "Top := T + 32;", what is the usage of it ?? Thanks so much for your help!
--------------------------
procedure TForm1.SpeedButton1Click(S
var
T: Integer;
begin
T := Top;
Self.Release;
Application.CreateForm(TFo
Top := T + 32;
Self.Show;
end;
--------------------------
procedure TForm1.SpeedButton1Click(S
var
F: TForm1;
begin
Release;
Application.CreateForm(TFo
F.Show;
end;
1). T gets the Top property value of thr current form before it is Released;
2). After a new instance is created, it's property Top is assigned as T + 32, i.e. 32 bits below ....
May be your Form has a property Position := poDeskTopCenter; and it is no effect ....
2). After a new instance is created, it's property Top is assigned as T + 32, i.e. 32 bits below ....
May be your Form has a property Position := poDeskTopCenter; and it is no effect ....
but
Top := T + 32;
was only for demonstration
Top := T + 32;
was only for demonstration
ASKER
> Position := poDeskTopCenter
Oh, yes.
So which procedure should I pick ??
"Application.CreateForm(TF orm1, Self);" or "Application.CreateForm(TF orm1, F);"
Thanks.
rng
Oh, yes.
So which procedure should I pick ??
"Application.CreateForm(TF
Thanks.
rng
In my opinion it does not matter which one will be in use, but if you use somewhere in the code something like that:
Form1........; // method or property usage
it is a must to use:
Application.CreateForm(TFo rm1, Self);
because in other case, you will have problems ....
Form1........; // method or property usage
it is a must to use:
Application.CreateForm(TFo
because in other case, you will have problems ....
ASKER
I found another problem, after the Form is being re-created, the application can not be closed completely. It still show up in the task manager even it has been terminated. Any idea to fix it ?
Thanks.
rng
Thanks.
rng
May be it is an unterminated instance of an unsuccessful attempt before
ASKER
Any method to prevent it happen ?
rng
rng
use:
Application.CreateForm(TFo rm1, Self);
and if it works every time o.k. there will not be unterminated instances.
If the problem persits, there will be unterminated instances in the memory...
I hope all is o.k. now !
Application.CreateForm(TFo
and if it works every time o.k. there will not be unterminated instances.
If the problem persits, there will be unterminated instances in the memory...
I hope all is o.k. now !
I see in the TaskBar Applications: Delphi_Programming: Restart button - Close the Form and re-create a new instance
but that is the Internet Explorer's instance that is open your question, but not unterminated instance of Delphi ....
but that is the Internet Explorer's instance that is open your question, but not unterminated instance of Delphi ....
ASKER
My problem is that even the application disappeared from the "Applications" tab of the task manager, but it still show up in "Processes" tab.
I have used "Application.CreateForm(TF orm1, Self);" now, but the problem is still here. Any idea?
Thanks.
rng
I have used "Application.CreateForm(TF
Thanks.
rng
I have not this problem or similar....
Until I press the button that Releases the Form1 and Creates a new TForm1 instance, the application is shown in the TaskManager Processes, but after I close it - there is no more instance of it ....
Did you close by TaskManager this instance, before run Delphi project again ? May be it is an old instance and it will be in the memory until you close it, or restart the Windows ....
Until I press the button that Releases the Form1 and Creates a new TForm1 instance, the application is shown in the TaskManager Processes, but after I close it - there is no more instance of it ....
Did you close by TaskManager this instance, before run Delphi project again ? May be it is an old instance and it will be in the memory until you close it, or restart the Windows ....
ASKER
Yes, I ended the process in task manager. Then, try it again. But the problem is still here.
Thanks.
rng
Thanks.
rng
???? I am very curious about your problem, but I just have not it ....
May be your third-party components continue to make problems for your application ?
May be your third-party components continue to make problems for your application ?
I will be back online after 10 - 12 hours
emil
emil
hello rng, I hope I can help, esoftbg's code seems workable, so maybe he gets the points, , The way that I understand the Release for a Form, is that this will relaese that Object and free any memory, that this Form object is using, so I would not place any Release or CreateForm for the same form , Form1 in this case, in that form's Object code block. . . . Although it is true that the Release method will allow the code to compleate, I would place it in a separate code block outside the Object code just for safty (but this may be unnessary). .
I think that the TApplication will record the first Form created as the Application.MainForm, and When you call Form1.Release , I do not think that the Application will release the MainForm and it may not replace the MainForm with the call for Application.CreateForm(TFo rm1, Form1); after Form1.Release . . . And usually if the MainForm Closes then the Application will Terminate, but since there may not be a MainForm anymore, , I would add code in the Form1 Close event to terminate the Application (with a Boolean for not on a Restart).
var
Form1: TForm1;
Kill: Boolean = True;
implementation
{$R *.DFM}
procedure RestartForm1;
begin
if Form1 <> nil then
begin
Kill := False;
Form1.Release;
Application.CreateForm(TFo rm1, Form1);
Form1.Show;
//Application.MainForm := Form1;
{can not reassign MainForm}
Kill := True;
end;
end;
procedure TForm1.but_RestartClick(Se nder: TObject);
begin
RestartForm1;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Kill then
Application.Terminate;
end;
I think that the TApplication will record the first Form created as the Application.MainForm, and When you call Form1.Release , I do not think that the Application will release the MainForm and it may not replace the MainForm with the call for Application.CreateForm(TFo
var
Form1: TForm1;
Kill: Boolean = True;
implementation
{$R *.DFM}
procedure RestartForm1;
begin
if Form1 <> nil then
begin
Kill := False;
Form1.Release;
Application.CreateForm(TFo
Form1.Show;
//Application.MainForm := Form1;
{can not reassign MainForm}
Kill := True;
end;
end;
procedure TForm1.but_RestartClick(Se
begin
RestartForm1;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Kill then
Application.Terminate;
end;
ASKER
Hello All,
I have figured out the problem. If I click the "cross" button in the top right hand corner after restart, the application can't be closed. But if I run the "Application.Terminate;", the process can be completely closed even after restart.
So I think I need to capture the OnClose event and write a "Application.Terminate;" as event handler.
Thanks.
rng
I have figured out the problem. If I click the "cross" button in the top right hand corner after restart, the application can't be closed. But if I run the "Application.Terminate;", the process can be completely closed even after restart.
So I think I need to capture the OnClose event and write a "Application.Terminate;" as event handler.
Thanks.
rng
Hi @Slick812,
@rng has a condition in his question: .... 'When users click on this button, I want the Form to "Close", and then a new instance of the same Form will be created (without terminating the application). How can I write it ??'
I made a simple application with only Form1, dropped on it 3 SpeedButtons, one Memo and one ListBox. All my examples above are tested with this Form1 and work fine:
//------------------------ ---------- ---------- ---------- ---------
procedure TForm1.SpeedButton1Click(S ender: TObject);
begin
Self.Release;
Application.CreateForm(TFo rm1, Self);
Self.Show;
end;
//------------------------ ---------- ---------- ---------- ---------
procedure TForm1.SpeedButton1Click(S ender: TObject);
var
F: TForm1;
begin
Release;
Application.CreateForm(TFo rm1, F);
F.Show;
end;
//------------------------ ---------- ---------- ---------- ---------
The second example is more safe, but only if he does not use direct call of the Form1 in his code, so I have not his code, I adviced him to use the first one ....
I extended my example adding a second Form2 and it continue to works fine even the Form2 is Shown (Visible).
So I have not any problems to Release and Recreate the main Form (Form1) in all cases I tested my example.
@rng uses some third-party components, so I think there is the reason of his troubles. May be he must Free all the third-party components on the event OnDestroy of the Form1 ....
Best Regards,
@esoftbg
@rng has a condition in his question: .... 'When users click on this button, I want the Form to "Close", and then a new instance of the same Form will be created (without terminating the application). How can I write it ??'
I made a simple application with only Form1, dropped on it 3 SpeedButtons, one Memo and one ListBox. All my examples above are tested with this Form1 and work fine:
//------------------------
procedure TForm1.SpeedButton1Click(S
begin
Self.Release;
Application.CreateForm(TFo
Self.Show;
end;
//------------------------
procedure TForm1.SpeedButton1Click(S
var
F: TForm1;
begin
Release;
Application.CreateForm(TFo
F.Show;
end;
//------------------------
The second example is more safe, but only if he does not use direct call of the Form1 in his code, so I have not his code, I adviced him to use the first one ....
I extended my example adding a second Form2 and it continue to works fine even the Form2 is Shown (Visible).
So I have not any problems to Release and Recreate the main Form (Form1) in all cases I tested my example.
@rng uses some third-party components, so I think there is the reason of his troubles. May be he must Free all the third-party components on the event OnDestroy of the Form1 ....
Best Regards,
@esoftbg
rng,
Use
OnCloseQuery
event
Use
OnCloseQuery
event
ASKER
Why should I use "OnCloseQuery" instead of "OnClose" ??
> @rng uses some third-party components, so I think there is the reason of his troubles. May be he must Free all the third-party components on the event OnDestroy of the Form1 ....
How can I do that ?? Any example?
Thanks so much for your help.
rng
> @rng uses some third-party components, so I think there is the reason of his troubles. May be he must Free all the third-party components on the event OnDestroy of the Form1 ....
How can I do that ?? Any example?
Thanks so much for your help.
rng
Excuse me ! I was busy ....
Please try this:
procedure TForm1.SpeedButton1Click(S ender: TObject);
begin
Release;
try
Application.ProcessMessage s;
finally
Application.CreateForm(TFo rm1, Self);
Self.Show;
end;
end;
Please try this:
procedure TForm1.SpeedButton1Click(S
begin
Release;
try
Application.ProcessMessage
finally
Application.CreateForm(TFo
Self.Show;
end;
end;
I will send you 3 different examples .... Please wait to develop them ....
sorry, I was only commenting, I did not say your code did not work, I tried to say that your code was good, the only new part of my code was the
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Kill then
Application.Terminate;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Kill then
Application.Terminate;
end;
Excuse me Slick812,
I did not understand Your first comment, but now I am absolutely sure that as You described after Releaseing of the original Form1 which is the MainForm of the Application, a new instance of Class TForm1 does not become as a MainForm, so the only way to close the Application on Close of any new Instance of TForm1 is as You described:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Buttons, StdCtrls;
type
TForm1 = class(TForm)
spbRestart: TSpeedButton;
Memo: TMemo;
ListBox: TListBox;
procedure spbRestartClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Killed:Boolean = False;
implementation
{$R *.dfm}
procedure TForm1.spbRestartClick(Sen der: TObject);
var
T: Integer;
F: TForm1;
begin
T := Top + 32;
Release;
Application.CreateForm(TFo rm1, F);
Killed := True;
F.Show;
if (T<Screen.Height-96) then
F.Top := T;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Killed then
Application.Terminate;
end;
end.
I did not understand Your first comment, but now I am absolutely sure that as You described after Releaseing of the original Form1 which is the MainForm of the Application, a new instance of Class TForm1 does not become as a MainForm, so the only way to close the Application on Close of any new Instance of TForm1 is as You described:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Buttons, StdCtrls;
type
TForm1 = class(TForm)
spbRestart: TSpeedButton;
Memo: TMemo;
ListBox: TListBox;
procedure spbRestartClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Killed:Boolean = False;
implementation
{$R *.dfm}
procedure TForm1.spbRestartClick(Sen
var
T: Integer;
F: TForm1;
begin
T := Top + 32;
Release;
Application.CreateForm(TFo
Killed := True;
F.Show;
if (T<Screen.Height-96) then
F.Top := T;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Killed then
Application.Terminate;
end;
end.
ASKER
Thanks esoftbg., I am waiting for them!!
rng
rng
rng,
Please download a project with 3 examples from:
page: http://www.geocities.com/esoftbg/
link: Q_21196960.zip
//........
I tried:
Application.MainForm.Assig n(Self);
but unfortunatelly it does not work ....
Please download a project with 3 examples from:
page: http://www.geocities.com/esoftbg/
link: Q_21196960.zip
//........
I tried:
Application.MainForm.Assig
but unfortunatelly it does not work ....
ASKER
Your examples work perfectly...
Last question:
> @rng uses some third-party components, so I think there is the reason of his troubles. May be he must Free all the third-party components on the event OnDestroy of the Form1 ....
How can I do that ??
Thanks.
rng
Last question:
> @rng uses some third-party components, so I think there is the reason of his troubles. May be he must Free all the third-party components on the event OnDestroy of the Form1 ....
How can I do that ??
Thanks.
rng
Something like that (the components I describe are not third-party, but the way is the same):
procedure TForm1.FormDestroy(Sender: TObject);
begin
ListBox.Free; // This way you should ftree the third-party component
Memo.Free; // This way you should ftree the third-party component
//.............// This way you should ftree the third-party component
end;
procedure TForm1.FormDestroy(Sender:
begin
ListBox.Free; // This way you should ftree the third-party component
Memo.Free; // This way you should ftree the third-party component
//.............// This way you should ftree the third-party component
end;
In reality @Slick812 solved the problem with that in the memory reside some instance of the Appication after closure of the current instance of the TForm1 (using a boolean variable Killed which knows the MainForm is closed or not), so if you have not problems after using this variable, you don't need freeing the third-party components ....
ASKER
Why do I need the Killed variable ?
"Application.Terminate;" will not hurt anything even the Form hasn't been "Killed" before, right ?
rng
"Application.Terminate;" will not hurt anything even the Form hasn't been "Killed" before, right ?
rng
yes you can use Application.Terminate at any time without any harm, the reason I included my Kill variable is so you can recreate your Form1 MORE than ONE time, otherwize with no Kill the whole app will be gone when you recreate Form1
Kill := False;
Form1.Release;
Application.CreateForm(TFo rm1, Form1);
Form1.Show;
Kill := True;
Kill := False;
Form1.Release;
Application.CreateForm(TFo
Form1.Show;
Kill := True;
ASKER
I don't use Kill variable, but I can still recreate the Form more than one time, here is my code:
procedure TfrmMain.cmdRestartClick(S ender: TObject);
begin
Self.Release;
Application.CreateForm(Tfr mMain, Self);
Self.Show;
end;
procedure TfrmMain.FormCloseQuery(Se nder: TObject; var CanClose: Boolean);
begin
CanClose := True;
Application.Terminate;
end;
procedure TfrmMain.cmdRestartClick(S
begin
Self.Release;
Application.CreateForm(Tfr
Self.Show;
end;
procedure TfrmMain.FormCloseQuery(Se
begin
CanClose := True;
Application.Terminate;
end;
procedure TForm1.SpeedButton1Click(S
var
T: Integer;
begin
T := Top;
Self.Release;
Application.CreateForm(TFo
Top := T + 32;
Self.Show;
end;