Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

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

Posted on 2006-11-02
14
416 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
Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 

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 500 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

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.

Question has a verified solution.

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

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.
In an interesting question (https://www.experts-exchange.com/questions/29008360/) here at Experts Exchange, a member asked how to split a single image into multiple images. The primary usage for this is to place many photographs on a flatbed scanner…

860 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