[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 275
  • Last Modified:

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
0
rng
Asked:
rng
  • 29
  • 19
  • 3
1 Solution
 
esoftbgCommented:
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;
0
 
esoftbgCommented:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  Release;
  Application.CreateForm(TForm1, Self);
  Show;
end;
0
 
rngAuthor Commented:
What is the difference between two approachs?
Could you give some remarks to the code?

Thanks.

rng
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
esoftbgCommented:
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.  
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
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 ????
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
If you can send me your project via e-mail, I'll try to fix it, but if not it is difficult to divine ....
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
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;
0
 
esoftbgCommented:
This is the best solution:

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  F:      TForm1;
begin
  Release;
  Application.CreateForm(TForm1, F);
  F.Show;
end;
0
 
rngAuthor Commented:
Unforunately, this doesn't solve the problem.  I think the "Release" function doesn't work as expected...
Thanks for your help.

rng
0
 
esoftbgCommented:
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 !
0
 
rngAuthor Commented:
Are you talking about the last sample code ? I tried it already, but the same problem arise.

rng
0
 
esoftbgCommented:
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.
0
 
esoftbgCommented:
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.
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
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;
0
 
esoftbgCommented:
I will go to the pharmacie and will be back in about 30 minutes ....
0
 
rngAuthor Commented:
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;

0
 
esoftbgCommented:
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 ....
0
 
esoftbgCommented:
but
  Top := T + 32;
was only for demonstration
0
 
rngAuthor Commented:
> Position := poDeskTopCenter
Oh, yes.

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

Thanks.
rng
0
 
esoftbgCommented:
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 ....
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
May be it is an unterminated instance of an unsuccessful attempt before
0
 
rngAuthor Commented:
Any method to prevent it happen ?

rng
0
 
esoftbgCommented:
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 !

0
 
esoftbgCommented:
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 ....
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
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 ....
0
 
rngAuthor Commented:
Yes, I ended the process in task manager. Then, try it again. But the problem is still here.
Thanks.

rng
0
 
esoftbgCommented:
???? 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 ?
0
 
esoftbgCommented:
I will be back online after 10 - 12 hours
emil
0
 
Slick812Commented:
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;
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
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
0
 
esoftbgCommented:
rng,

Use
  OnCloseQuery
event
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
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;
0
 
esoftbgCommented:
I will send you 3 different examples .... Please wait to develop them ....
0
 
Slick812Commented:
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;
0
 
esoftbgCommented:
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.
0
 
rngAuthor Commented:
Thanks esoftbg., I am waiting for them!!

rng
0
 
esoftbgCommented:
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 ....
0
 
rngAuthor Commented:
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
0
 
esoftbgCommented:
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;
0
 
esoftbgCommented:
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 ....
0
 
rngAuthor Commented:
Why do I need the Killed variable ?
"Application.Terminate;" will not hurt anything even the Form hasn't been "Killed" before, right ?

rng
0
 
Slick812Commented:
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;
0
 
rngAuthor Commented:
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;
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 29
  • 19
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now