Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

Can't close a form with biSystemMenu X button when loop executing

Posted on 2006-11-02
14
Medium Priority
?
437 Views
Last Modified: 2010-04-04
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.ProcessMessages 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.
0
Comment
Question by:cyman73
  • 8
  • 4
  • 2
14 Comments
 
LVL 28

Expert Comment

by:TName
ID: 17861461
Hi, try this (show this form modal):


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(GetTickCount);
    Application.ProcessMessages;
 end;
end;

procedure TForm2.WMNCLButtonDown(var Message: TWMNCLButtonDown);
begin
   inherited;
   Stop:=True;
end;

procedure TForm2.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:=False;
  Animate;
end;

end.
0
 
LVL 28

Expert Comment

by:TName
ID: 17861657
Changed the WMNCLButtonDown:

procedure TForm2.WMNCLButtonDown(var Message: TWMNCLButtonDown);
begin
   if (Message.HitTest = HTCLOSE) then
     Stop:=True;
   inherited;
end;
0
 
LVL 28

Expert Comment

by:TName
ID: 17861869
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;

0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 

Author Comment

by:cyman73
ID: 17861939
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.


0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 17861949
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.ProcessMessages;
  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.
0
 
LVL 28

Expert Comment

by:TName
ID: 17862213
>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:=TFrmAnimation.Create(nil);
  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...
0
 

Author Comment

by:cyman73
ID: 17862526
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.
0
 
LVL 28

Expert Comment

by:TName
ID: 17862547
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(Sender: 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(GetTickCount);
    Application.ProcessMessages;
 end;
end;

procedure TFrmAnimation.WMNCLButtonDown(var Message: TWMNCLButtonDown);
begin
   if (Message.HitTest = HTCLOSE) and TForm(Self).Showing then
     Stop:=True;
   inherited;
end;

procedure TFrmAnimation.TimerProc(Sender: TObject);
begin
  Timer.Enabled:=False;
  Animate;
end;

procedure TFrmAnimation.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Timer.Free;
end;

end.
0
 
LVL 28

Accepted Solution

by:
TName earned 2000 total points
ID: 17862849
>Besides many other software packages use this call, so I didn't want to update all of them.
Ok, I understand.

The problem is that the modal window is still there (not freed) when you close it, so everything (variables, labels) should be reset in the OnShow procedure, because that procedure will fire when you show the form again.

BTW, a last version of WMNCLButtonDown ;)

procedure TFrmAnimation.WMNCLButtonDown(var Message: TWMNCLButtonDown);
begin
   if (Message.HitTest = HTCLOSE) then
     Stop:=True;
   Application.ProcessMessages;
   inherited;
end;
0
 
LVL 28

Expert Comment

by:TName
ID: 17863105
>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...
0
 

Author Comment

by:cyman73
ID: 17863571
Hey TName,
Does your implementation in essence put the animation in its own thread?

0
 
LVL 28

Expert Comment

by:TName
ID: 17865031
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.
0
 

Author Comment

by:cyman73
ID: 17866877
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.
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 17872544
You're welcome
0

Featured Post

Industry Leaders: 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!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
this video summaries big data hadoop online training demo (http://onlineitguru.com/big-data-hadoop-online-training-placement.html) , and covers basics in big data hadoop .
Are you ready to place your question in front of subject-matter experts for more timely responses? With the release of Priority Question, Premium Members, Team Accounts and Qualified Experts can now identify the emergent level of their issue, signal…
Suggested Courses

971 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question