cyman73
asked on
Can't close a form with biSystemMenu X button when loop executing
Hello all,
I call a form in my main application the following way: FrmAnimation.ShowModal;
From the FrmAnimation form I execute an Animation Procedure. While this Animation Procedure ( a while loop executing away) is active, I cannot close the form if I want to exit by pressing the X button from the System Menu. I made sure to add Application.ProcessMessage s statements in the while loop, however the close action is not registered until I stop the while loop/Animation Procedure. The while loop is stopped if I click anywhere on the form. I click on the X button, while animation is ON, the form closes immediately after clicking on the form itself.
OnClose and OnCloseQuery are not executed when this while loop is executing.
Can anyone give me any ideas on how to close this form when animation is ON. I want to give the user the ability to close the form whenever they click the X button without delay. Any help is greatly appreciated.
Thank you.
I call a form in my main application the following way: FrmAnimation.ShowModal;
From the FrmAnimation form I execute an Animation Procedure. While this Animation Procedure ( a while loop executing away) is active, I cannot close the form if I want to exit by pressing the X button from the System Menu. I made sure to add Application.ProcessMessage
OnClose and OnCloseQuery are not executed when this while loop is executing.
Can anyone give me any ideas on how to close this form when animation is ON. I want to give the user the ability to close the form whenever they click the X button without delay. Any help is greatly appreciated.
Thank you.
Changed the WMNCLButtonDown:
procedure TForm2.WMNCLButtonDown(var Message: TWMNCLButtonDown);
begin
if (Message.HitTest = HTCLOSE) then
Stop:=True;
inherited;
end;
procedure TForm2.WMNCLButtonDown(var
begin
if (Message.HitTest = HTCLOSE) then
Stop:=True;
inherited;
end;
BTW, if you do this, don't let Form2 be auto-created. (Project>Options>Forms - Auto-create Forms or delete its creation in the .dpr)
Create it and free it when needed, otherwise it might mess up the main form's message handling.
e.g.
with TForm2.Create(nil) do begin
ShowModal;
Free;
end;
Create it and free it when needed, otherwise it might mess up the main form's message handling.
e.g.
with TForm2.Create(nil) do begin
ShowModal;
Free;
end;
ASKER
Sorry for being so green.
I think I understand what is going in the code you suggested and I'm trying to implement it.
With regards to your last comment, I can't make any changes to the main form. I need to implement the changes in the FrmAnimation only and so I need to leavel FrmAnimation.ShowModal as it was.
If I use the WMNCLButtonDown() procedure, will this then catch any mouse down events?
I wonder why the mouse event is not "registered" when clicking on the title bar/system menu, at least when the loop is executing.
Should find out if it works soon.
I think I understand what is going in the code you suggested and I'm trying to implement it.
With regards to your last comment, I can't make any changes to the main form. I need to implement the changes in the FrmAnimation only and so I need to leavel FrmAnimation.ShowModal as it was.
If I use the WMNCLButtonDown() procedure, will this then catch any mouse down events?
I wonder why the mouse event is not "registered" when clicking on the title bar/system menu, at least when the loop is executing.
Should find out if it works soon.
Or you could just intercept the WM_CLOSE message in the WndProc method. e.g.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls;
type
TForm1 = class(TForm)
ProgressBar1: TProgressBar;
Button1: TButton;
procedure FormClick(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
Animate: boolean;
procedure DoAnimation;
protected
procedure WndProc(var Message: TMessage); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.DoAnimation;
begin
while Animate do
begin
if ProgressBar1.Position = ProgressBar1.Max
then ProgressBar1.Position:= ProgressBar1.Min;
ProgressBar1.StepIt;
Application.ProcessMessage s;
end;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
Animate:= false;
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
Animate:= true;
DoAnimation;
end;
procedure TForm1.WndProc(var Message: TMessage);
begin
if Message.Msg = WM_CLOSE
then Animate:= false;
inherited;
end;
end.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls;
type
TForm1 = class(TForm)
ProgressBar1: TProgressBar;
Button1: TButton;
procedure FormClick(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
Animate: boolean;
procedure DoAnimation;
protected
procedure WndProc(var Message: TMessage); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.DoAnimation;
begin
while Animate do
begin
if ProgressBar1.Position = ProgressBar1.Max
then ProgressBar1.Position:= ProgressBar1.Min;
ProgressBar1.StepIt;
Application.ProcessMessage
end;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
Animate:= false;
end;
procedure TForm1.Button1Click(Sender
begin
Animate:= true;
DoAnimation;
end;
procedure TForm1.WndProc(var Message: TMessage);
begin
if Message.Msg = WM_CLOSE
then Animate:= false;
inherited;
end;
end.
>If I use the WMNCLButtonDown() procedure, will this then catch any mouse down events?
Not the regular mouse down, only the left clicks in the non client area (mainly titlebar).
>I can't make any changes to the main form.
If you cannot even replace
FrmAnimation.ShowModal;
with
FrmAnimation:=TFrmAnimatio n.Create(n il);
FrmAnimation.ShowModal;
FrmAnimation.Free;
in your main form (may I ask why?), then you should probably better try another method. PierreC's solution might be better suited...
Not the regular mouse down, only the left clicks in the non client area (mainly titlebar).
>I can't make any changes to the main form.
If you cannot even replace
FrmAnimation.ShowModal;
with
FrmAnimation:=TFrmAnimatio
FrmAnimation.ShowModal;
FrmAnimation.Free;
in your main form (may I ask why?), then you should probably better try another method. PierreC's solution might be better suited...
ASKER
in your main form (may I ask why?), then you should probably better try another method. PierreC's solution might be better suited...
So this will update a portion of an application. The update is only for the animation. Besides many other software packages use this call, so I didn't want to update all of them.
What is the difference between
WndProc and WMNCLButtonDown
I got bot implementations working but I have an issue. It seems that if I close the FrmAnimation multiple times, then I try to open it, the forms opens and closes right away before any user input. This with WMNCLButtonDown. Should I be flushing this message or consuming it somehow?
I tried WndProc, but same issue. The window closes by itself just after opening after multiple open/close cycles.
Thanks.
So this will update a portion of an application. The update is only for the animation. Besides many other software packages use this call, so I didn't want to update all of them.
What is the difference between
WndProc and WMNCLButtonDown
I got bot implementations working but I have an issue. It seems that if I close the FrmAnimation multiple times, then I try to open it, the forms opens and closes right away before any user input. This with WMNCLButtonDown. Should I be flushing this message or consuming it somehow?
I tried WndProc, but same issue. The window closes by itself just after opening after multiple open/close cycles.
Thanks.
Ok, creating at least the timer at runtime seems to solve the problem.
This should work with
FrmAnimation.ShowModal;
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TFrmAnimation = class(TForm)
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
Stop:Boolean;
Timer:TTimer;
procedure Animate;
procedure TimerProc(Sender: TObject);
procedure WMNCLButtonDown(var Message: TWMNCLButtonDown); message WM_NCLBUTTONDOWN;
public
{ Public declarations }
end;
var
FrmAnimation:TFrmAnimation ;
implementation
{$R *.dfm}
procedure TFrmAnimation.FormShow(Sen der: TObject);
begin
Timer:=TTimer.Create(Nil);
with Timer do begin
Interval:=1000;
Enabled:=True;
OnTimer:=TimerProc;
end;
end;
procedure TFrmAnimation.Animate;
var
i:Integer;
begin
Stop:=False;
i:=GetTickCount;
while (GetTickCount < i+5000) and (Stop=false) do begin
Caption:=IntToStr(GetTickC ount);
Application.ProcessMessage s;
end;
end;
procedure TFrmAnimation.WMNCLButtonD own(var Message: TWMNCLButtonDown);
begin
if (Message.HitTest = HTCLOSE) and TForm(Self).Showing then
Stop:=True;
inherited;
end;
procedure TFrmAnimation.TimerProc(Se nder: TObject);
begin
Timer.Enabled:=False;
Animate;
end;
procedure TFrmAnimation.FormClose(Se nder: TObject;
var Action: TCloseAction);
begin
Timer.Free;
end;
end.
This should work with
FrmAnimation.ShowModal;
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TFrmAnimation = class(TForm)
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
Stop:Boolean;
Timer:TTimer;
procedure Animate;
procedure TimerProc(Sender: TObject);
procedure WMNCLButtonDown(var Message: TWMNCLButtonDown); message WM_NCLBUTTONDOWN;
public
{ Public declarations }
end;
var
FrmAnimation:TFrmAnimation
implementation
{$R *.dfm}
procedure TFrmAnimation.FormShow(Sen
begin
Timer:=TTimer.Create(Nil);
with Timer do begin
Interval:=1000;
Enabled:=True;
OnTimer:=TimerProc;
end;
end;
procedure TFrmAnimation.Animate;
var
i:Integer;
begin
Stop:=False;
i:=GetTickCount;
while (GetTickCount < i+5000) and (Stop=false) do begin
Caption:=IntToStr(GetTickC
Application.ProcessMessage
end;
end;
procedure TFrmAnimation.WMNCLButtonD
begin
if (Message.HitTest = HTCLOSE) and TForm(Self).Showing then
Stop:=True;
inherited;
end;
procedure TFrmAnimation.TimerProc(Se
begin
Timer.Enabled:=False;
Animate;
end;
procedure TFrmAnimation.FormClose(Se
var Action: TCloseAction);
begin
Timer.Free;
end;
end.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
>What is the difference between
>WndProc and WMNCLButtonDown
Very simply put:
Intercepting the messages sent by windows to the form (by overriding WndProc), you can filter the messages and react if a certain message is sent. In this case, if the message is "close", then first set variable x to value y.
WMNCLButtonDown handles the message that is sent when the user clicks on the non-client area of the window. In this case, you can say: if the click was on the close button, then first do this and that...
>WndProc and WMNCLButtonDown
Very simply put:
Intercepting the messages sent by windows to the form (by overriding WndProc), you can filter the messages and react if a certain message is sent. In this case, if the message is "close", then first set variable x to value y.
WMNCLButtonDown handles the message that is sent when the user clicks on the non-client area of the window. In this case, you can say: if the click was on the close button, then first do this and that...
ASKER
Hey TName,
Does your implementation in essence put the animation in its own thread?
Does your implementation in essence put the animation in its own thread?
You mean, because of the timer? No, that would be a different matter.
It just makes sure the while loop is stopped in time.
The timer is just used to start the loop.
It just makes sure the while loop is stopped in time.
The timer is just used to start the loop.
ASKER
Thank you for your help. I was able to fix this issue using your suggestion TName. Thank you to Pierre for your suggestion as well.
You're welcome
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm2 = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
private
Stop:Boolean;
procedure Animate;
procedure WMNCLButtonDown(var Message: TWMNCLButtonDown); message WM_NCLBUTTONDOWN;
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Animate;
var
i:Integer;
begin
Stop:=False;
i:=GetTickCount;
while (GetTickCount < i+5000) and (Stop=false) do begin
Caption:=IntToStr(GetTickC
Application.ProcessMessage
end;
end;
procedure TForm2.WMNCLButtonDown(var
begin
inherited;
Stop:=True;
end;
procedure TForm2.Timer1Timer(Sender:
begin
Timer1.Enabled:=False;
Animate;
end;
end.