Link to home
Start Free TrialLog in
Avatar of ivobauer
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.
ASKER CERTIFIED SOLUTION
Avatar of nrico
nrico

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of nrico
nrico

I obviously mean "second"... :-)
You can also use OnShow and OnPaint.
Avatar of ivobauer

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

you can override the 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):Integer;
   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
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
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.processMessages between the time you add the message to Form2's event queue and the time you call showModal.

2.  Don't call application.processMessages 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.processMessages, it will probably be esier to consider the earlier approaches.

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

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(var Message: TMessage);
begin
  try
    try
      WndProc(Message);
    finally
      FreeDeviceContexts;
      FreeMemoryContexts;
    end;
  except
    Application.HandleException(Self);
  end;
end;

Rick

 
Well, I'll give my points to nrico even all of the answers were acceptable. Thanks to all.