Link to home
Start Free TrialLog in
Avatar of rng
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
Avatar of esoftbg
esoftbg
Flag of Bulgaria image

I tested next code, it works fine:

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  T:      Integer;
begin
  T := Top;
  Self.Release;
  Application.CreateForm(TForm1, Self);
  Top := T + 32;
  Self.Show;
end;
ASKER CERTIFIED SOLUTION
Avatar of esoftbg
esoftbg
Flag of Bulgaria image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of rng
rng

ASKER

What is the difference between two approachs?
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.  
Avatar of rng

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
The method:    Release kills the previous instance
The method:    Application.CreateForm(TForm1, Self); creates a new one ....
???? There is no two instance at the same time ????
Avatar of rng

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
If you can send me your project via e-mail, I'll try to fix it, but if not it is difficult to divine ....
Avatar of rng

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
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(Sender: TObject);
begin
  Release;
  Sleep(100); // or Sleep(400);
  Application.CreateForm(TForm1, Self);
  Show;
end;
This is the best solution:

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  F:      TForm1;
begin
  Release;
  Application.CreateForm(TForm1, F);
  F.Show;
end;
Avatar of rng

ASKER

Unforunately, this doesn't solve the problem.  I think the "Release" function doesn't work as expected...
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 !
Avatar of rng

ASKER

Are you talking about the last sample code ? I tried it already, but the same problem arise.

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.
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.
Avatar of rng

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
O.k., try 2 variants:

1). Without Release:
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  F:      TForm1;
begin
  Hide; // Hides the current Form without destroying it;
  Application.CreateForm(TForm1, F); // Creates a new independent Form;
  F.Show;
end;

2). First create a new Form, then destroy the current one:
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  F:      TForm1;
begin
  Application.CreateForm(TForm1, F);
  Release;
  F.Show;
end;
I will go to the pharmacie and will be back in about 30 minutes ....
Avatar of rng

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(Sender: TObject);
var
  T:      Integer;
begin
  T := Top;
  Self.Release;
  Application.CreateForm(TForm1, Self);
  Top := T + 32;
  Self.Show;
end;

-------------------------------------------
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  F:      TForm1;
begin
  Release;
  Application.CreateForm(TForm1, F);
  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 ....
but
  Top := T + 32;
was only for demonstration
Avatar of rng

ASKER

> Position := poDeskTopCenter
Oh, yes.

So which procedure should I pick ??  
"Application.CreateForm(TForm1, Self);"  or  "Application.CreateForm(TForm1, F);"

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(TForm1, Self);
because in other case, you will have problems ....
Avatar of rng

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
May be it is an unterminated instance of an unsuccessful attempt before
Avatar of rng

ASKER

Any method to prevent it happen ?

rng
use:
  Application.CreateForm(TForm1, 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 !

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 ....
Avatar of rng

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(TForm1, Self);" now, but the problem is still here. Any idea?

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 ....
Avatar of rng

ASKER

Yes, I ended the process in task manager. Then, try it again. But the problem is still here.
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 ?
I will be back online after 10 - 12 hours
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(TForm1, 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(TForm1, Form1);
  Form1.Show;
  //Application.MainForm := Form1;
    {can not reassign MainForm}
  Kill := True;
  end;
end;


procedure TForm1.but_RestartClick(Sender: TObject);
begin
RestartForm1;
end;


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Kill then
  Application.Terminate;
end;
Avatar of rng

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
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(Sender: TObject);
begin
  Self.Release;
  Application.CreateForm(TForm1, Self);
  Self.Show;
end;

//---------------------------------------------------------------
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  F:      TForm1;
begin
  Release;
  Application.CreateForm(TForm1, 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,

Use
  OnCloseQuery
event
Avatar of rng

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
Excuse me ! I was busy ....
Please try this:

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  Release;
  try
    Application.ProcessMessages;
  finally
    Application.CreateForm(TForm1, Self);
    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;
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(Sender: TObject);
var
  T:      Integer;
  F:      TForm1;
begin
  T := Top + 32;
  Release;
  Application.CreateForm(TForm1, 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.
Avatar of rng

ASKER

Thanks esoftbg., I am waiting for them!!

rng
rng,
Please download a project with 3 examples from:
page:        http://www.geocities.com/esoftbg/
  link:        Q_21196960.zip

//........
I tried:
  Application.MainForm.Assign(Self);
but unfortunatelly it does not work ....
Avatar of rng

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
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;
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 ....
Avatar of rng

ASKER

Why do I need the Killed variable ?
"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(TForm1, Form1);
  Form1.Show;
  Kill := True;
Avatar of rng

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(Sender: TObject);
begin
  Self.Release;
  Application.CreateForm(TfrmMain, Self);
  Self.Show;
end;

procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
    CanClose := True;
    Application.Terminate;
end;