ivobauer
asked on
How to trigger an event...?
Hi experts.
I have two forms. Form1 is a main form. I want to show modally Form2 by, for example clicking on the button which is dropped on Form1:
procedure TForm1.ButtonClick(Sender: TObject);
begin
// ...
Form2.ShowModal
// ...
end;
There is no problem. But I want to somehow trigger an event in Form2 immediately after it is created and properly displayed. Can you tell me how to do this?
Thanks, Ivo.
I have two forms. Form1 is a main form. I want to show modally Form2 by, for example clicking on the button which is dropped on Form1:
procedure TForm1.ButtonClick(Sender:
begin
// ...
Form2.ShowModal
// ...
end;
There is no problem. But I want to somehow trigger an event in Form2 immediately after it is created and properly displayed. Can you tell me how to do this?
Thanks, Ivo.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I obviously mean "second"... :-)
You can also use OnShow and OnPaint.
ASKER
I know that ways but they look dirty. I want to keep this event handlers (OnActivate, OnShow...) untouched. Can you tell me more "cleaner" way to do this (if any)?
Why do they look dirty?
You want to do something, when form2 is shown. So OnShow is the cleanest thing I could think of. I don't understand why you don't like that...
You want to do something, when form2 is shown. So OnShow is the cleanest thing I could think of. I don't understand why you don't like that...
To do it any cleaner I would think you need to create a new TForm descendent with a new event and and added property for the first time thing...
you will make a property on your form2, then OnShow event verify the value of that property then execute watherver procedure or event that you want...
//******* form2
TForm2 = class(TForm)
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
FReason : Integer;
procedure proc1;
procedure Proc2(Sender: TObject);
public
{ Public declarations }
property Reason :Integer read FReason write FReason Default 0;
end;
procedure TForm2.FormShow(Sender: TObject);
begin
case FReason of
1: Proc1;
2: Proc2(Sender);
3: Button1Click(Sender);
end;
end;
procedure TForm2.proc1;
begin
// do your things
end;
procedure TForm2.proc1;
begin
// do your things
end;
procedure TForm2.Button1Click(Sender : TObject);
begin
end;
//******* form1
procedure TForm1.ButtonClick(Sender: TObject);
begin
// ...
Form2.Reason = 1; //to call proc1
Form2.ShowModal;
Form2.Reason = 3; // to emulate button click ;-)
Form2.ShowModal;
// ...
end;
even to make your code more "clear" you will make constant for your "Reason" property... something like rsProc1, rsButton1, etc... in that case then you assign
Form2.Reason = rsProc1;//to call proc1
Form2.ShowModal;
instead of
Form2.Reason = 1; //to call proc1
Form2.ShowModal;
if you want to call more than one procedure but still have the ability of selected by the property then you must use a "Set" instead of a integer and then modify your case statement....
good luck,
luis
//******* form2
TForm2 = class(TForm)
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
FReason : Integer;
procedure proc1;
procedure Proc2(Sender: TObject);
public
{ Public declarations }
property Reason :Integer read FReason write FReason Default 0;
end;
procedure TForm2.FormShow(Sender: TObject);
begin
case FReason of
1: Proc1;
2: Proc2(Sender);
3: Button1Click(Sender);
end;
end;
procedure TForm2.proc1;
begin
// do your things
end;
procedure TForm2.proc1;
begin
// do your things
end;
procedure TForm2.Button1Click(Sender
begin
end;
//******* form1
procedure TForm1.ButtonClick(Sender:
begin
// ...
Form2.Reason = 1; //to call proc1
Form2.ShowModal;
Form2.Reason = 3; // to emulate button click ;-)
Form2.ShowModal;
// ...
end;
even to make your code more "clear" you will make constant for your "Reason" property... something like rsProc1, rsButton1, etc... in that case then you assign
Form2.Reason = rsProc1;//to call proc1
Form2.ShowModal;
instead of
Form2.Reason = 1; //to call proc1
Form2.ShowModal;
if you want to call more than one procedure but still have the ability of selected by the property then you must use a "Set" instead of a integer and then modify your case statement....
good luck,
luis
you can override the method:
private
procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE;
But do not forget to call the inherited method...
private
procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE;
But do not forget to call the inherited method...
Also TForm component overrides this methot to handle activation/deactivation. It calls SetActive method, and I think set SetActive runs the OnActivate event...
Ups! I exchange the KISS (Keep it simple and stupid) to OOP (Object Oriented Programming);
now here is the simple one...
{*****Form2}
TForm2 = class(TForm)
public
function ShowMe(Par:Integer):Intege r;
end;
{implementation...}
function TForm2.ShowMe(Par: Integer): Integer;
begin
case par of
1: ShowMessage('Luis 1');
2: ShowMessage('Luis 2');
3: ShowMessage('Luis 3');
end;
result := self.ShowModal;
end;
(********Form1)
procedure TForm1.ButtonClick(Sender: TObject);
begin
// ...
form2.ShowMe(1);
{or...}
form2.ShowMe(2);
// ...
end;
good luck againg,
luis
now here is the simple one...
{*****Form2}
TForm2 = class(TForm)
public
function ShowMe(Par:Integer):Intege
end;
{implementation...}
function TForm2.ShowMe(Par: Integer): Integer;
begin
case par of
1: ShowMessage('Luis 1');
2: ShowMessage('Luis 2');
3: ShowMessage('Luis 3');
end;
result := self.ShowModal;
end;
(********Form1)
procedure TForm1.ButtonClick(Sender:
begin
// ...
form2.ShowMe(1);
{or...}
form2.ShowMe(2);
// ...
end;
good luck againg,
luis
I think the problem is you really don't understand what an event is...
lets look at a declaration
....
private
FMyEvent: TNotifyEvent;
(*just a pointer to procedure of object with one parameter Sender*)
....
protected
procedure MyEvent;
(*a procedure...nothing more*)
....
published
property OnMyEvent: TNotifyEvent read FMyEvent write FMyEvent;
(*a property with read and write handlers...when assigned will call the appropriate read and write...this is your standard event property*)
....
implementation...
procedure Anything;
begin
....
MyEvent;
....
end;
procedure MyEvent;
begin
if Assigned(FMyEvent) then
FMyEvent(self);
end;
Somewhere in your code your going to have to call MyEvent...whether this is in another event like onShow...or in your own WndProc...
You could override the WndProc...look at each message coming through and see if is the appropriate message if so call your event handler...
i.e.
procedure
TMyObject.WndProc(var Message:TMessage);
begin
if aMessage.Msg = WM_CLOSE then
begin
MyEvent;
end;
inherited WndProc;
end;
Rick
lets look at a declaration
....
private
FMyEvent: TNotifyEvent;
(*just a pointer to procedure of object with one parameter Sender*)
....
protected
procedure MyEvent;
(*a procedure...nothing more*)
....
published
property OnMyEvent: TNotifyEvent read FMyEvent write FMyEvent;
(*a property with read and write handlers...when assigned will call the appropriate read and write...this is your standard event property*)
....
implementation...
procedure Anything;
begin
....
MyEvent;
....
end;
procedure MyEvent;
begin
if Assigned(FMyEvent) then
FMyEvent(self);
end;
Somewhere in your code your going to have to call MyEvent...whether this is in another event like onShow...or in your own WndProc...
You could override the WndProc...look at each message coming through and see if is the appropriate message if so call your event handler...
i.e.
procedure
TMyObject.WndProc(var Message:TMessage);
begin
if aMessage.Msg = WM_CLOSE then
begin
MyEvent;
end;
inherited WndProc;
end;
Rick
Ivo,
Expanding on Ivo's approach, you could also:
1. Override Form2's wndProc method and define handling for a custom message.
2. Use the postMessage API function to place a reference to that message at the end of Form2's event queue.
Here's one way to do Step 1:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs;
type
TForm2 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
procedure wndProc( var aMessage : tMessage ); override;
end;
var
Form2: TForm2;
implementation
{$R *.DFM}
procedure tForm2.wndProc( var aMessage : tMessage );
begin
If ( aMessage.msg = WM_USER + 1 ) then
showMessage( 'Triggered by postMessage...' )
else
inherited wndProc( aMessage );
end;
end.
Once you've done this, trigger your custom message just before showing form2 modally. For example:
procedure TForm1.Button1Click(Sender : TObject);
begin
with tForm2.create( self ) do
try
postMessage( Handle, WM_USER + 1, 0, 0 );
showModal;
finally
release;
end; // with/try
end;
I'm not certain it's a better approach than the OnActivate ones posted earlier. However, based on your earlier reactions to those suggestions, this fit your need a little better.
Keep in mind a couple of considerations while working with this:
1. Remember _not_ to call application.processMessage s between the time you add the message to Form2's event queue and the time you call showModal.
2. Don't call application.processMessage s in any of the form activation/display handlers; otherwise, Form2 will process its pending events, including your custom one. (Your original post was very clear in that you wanted the form displayed first.)
3. If you have to have application.processMessage s, it will probably be esier to consider the earlier approaches.
Hope this helps...
-- Lance
Expanding on Ivo's approach, you could also:
1. Override Form2's wndProc method and define handling for a custom message.
2. Use the postMessage API function to place a reference to that message at the end of Form2's event queue.
Here's one way to do Step 1:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs;
type
TForm2 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
procedure wndProc( var aMessage : tMessage ); override;
end;
var
Form2: TForm2;
implementation
{$R *.DFM}
procedure tForm2.wndProc( var aMessage : tMessage );
begin
If ( aMessage.msg = WM_USER + 1 ) then
showMessage( 'Triggered by postMessage...' )
else
inherited wndProc( aMessage );
end;
end.
Once you've done this, trigger your custom message just before showing form2 modally. For example:
procedure TForm1.Button1Click(Sender
begin
with tForm2.create( self ) do
try
postMessage( Handle, WM_USER + 1, 0, 0 );
showModal;
finally
release;
end; // with/try
end;
I'm not certain it's a better approach than the OnActivate ones posted earlier. However, based on your earlier reactions to those suggestions, this fit your need a little better.
Keep in mind a couple of considerations while working with this:
1. Remember _not_ to call application.processMessage
2. Don't call application.processMessage
3. If you have to have application.processMessage
Hope this helps...
-- Lance
All,
Oops, I just realized that I mis-credited the Rick's and Luis's ideas in my previous comment. Accept my apologies...
-- Lance
Oops, I just realized that I mis-credited the Rick's and Luis's ideas in my previous comment. Accept my apologies...
-- Lance
I noticed this works from jumping to event handlers, although i dont know why it should just not bomb out..... wow 3 bombs like the ST... :)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender : TObject);
begin
close;
end;
procedure TForm1.Button2Click(Sender : TObject);
begin
asm
jmp tform1.button1click;
end;
end;
end.
anyone tell me why this works? and it seems to do the trick...
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender
begin
close;
end;
procedure TForm1.Button2Click(Sender
begin
asm
jmp tform1.button1click;
end;
end;
end.
anyone tell me why this works? and it seems to do the trick...
don't touch WndProc if you really don't need. Delphi's event handler does it better and simple.
I think creating a new virtual WndProc will slow down the others, because of calling inherited WndProc's each time unrelevant message accours.
I think creating a new virtual WndProc will slow down the others, because of calling inherited WndProc's each time unrelevant message accours.
You don't want to do a lot of coding in a Wndproc...but sometimes this is the only way to do things...it gives you a chance to see messages that you may not be able to see...
All you are doing is tagging into a method call that is already being called by TWinControl.MainWndProc
which looks like this...
procedure TWinControl.MainWndProc(va r Message: TMessage);
begin
try
try
WndProc(Message);
finally
FreeDeviceContexts;
FreeMemoryContexts;
end;
except
Application.HandleExceptio n(Self);
end;
end;
Rick
All you are doing is tagging into a method call that is already being called by TWinControl.MainWndProc
which looks like this...
procedure TWinControl.MainWndProc(va
begin
try
try
WndProc(Message);
finally
FreeDeviceContexts;
FreeMemoryContexts;
end;
except
Application.HandleExceptio
end;
end;
Rick
ASKER
Well, I'll give my points to nrico even all of the answers were acceptable. Thanks to all.