Recent

Author Topic: how to assign a regular procedure into an event handler?  (Read 14179 times)

bee

  • Sr. Member
  • ****
  • Posts: 393
how to assign a regular procedure into an event handler?
« on: January 30, 2016, 05:12:38 am »
Hi all,

I got a little problem here. I'd like to assign a regular procedure into an event handler. Say I have a TForm with a TTimer. But, instead of assign the timer's event on design time, I want to assign it on run time with a regular procedure.

I've tried to do this:
Code: Pascal  [Select][+][-]
  1. ...
  2. procedure doSometing(Sender: TObject);
  3. begin
  4.   ...
  5. end;
  6.  
  7. ...
  8. ...
  9. Timer1.OnTimer := @doSomething;
  10. ...

but it failed with "incompatible types" error. I have been searching the net without any satisfying answers because all them are using class procedure, not regular procedure.

Any hints? Or is it even possible to do so?

Thank you.

Regards,

-Bee
-Bee-

A long time pascal lover.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9908
  • Debugger - SynEdit - and more
    • wiki
Re: how to assign a regular procedure into an event handler?
« Reply #1 on: January 30, 2016, 05:19:25 am »
Those events a defined as methods. That is the should belong to an object

Code: Pascal  [Select][+][-]
  1. class MyClass;
  2.    procedure doSometing(Sender: TObject);
  3. end;
  4. procedure TMyClass.doSometing(Sender: TObject);
  5. begin
  6. end;  
  7. ...
  8. ...
  9. Timer1.OnTimer := @doSomething;
  10.  

Or define it on your TForm1 (but really any object you have is fine)

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: how to assign a regular procedure into an event handler?
« Reply #2 on: January 30, 2016, 05:26:15 am »
No you can't but you can work around it something along the lines of

Code: Delphi  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls, ExtCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TEventProc = procedure(aSender:Tobject);
  15.   //timer1.onTime is already connected to Timer1Timer through the object inspector.
  16.   TForm1 = class(TForm)
  17.     TabControl1 :TTabControl;
  18.     Timer1 :TTimer;
  19.     procedure TabControl1DrawTab(Control :TCustomTabControl; TabIndex :Integer; const Rect :TRect; AActive :Boolean);
  20.     procedure Timer1Timer(Sender :TObject);
  21.   private
  22.     FOnTimer :TEventProc;
  23.     procedure SetOnTimer(aValue :TEventProc);
  24.     { private declarations }
  25.   public
  26.     { public declarations }
  27.    
  28.     property OnTimer : TEventProc read FOnTimer write SetOnTimer;
  29.   end;
  30.  
  31. var
  32.   Form1 : TForm1;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38. { TForm1 }
  39.  
  40. procedure DoSomething(Sender:TObject);
  41. begin
  42.   //....
  43. end;
  44.  
  45. procedure TForm1.TabControl1DrawTab(Control :TCustomTabControl; TabIndex :Integer; const Rect :TRect; AActive :Boolean);
  46. begin
  47.   //
  48.   Control.Parent.;
  49.   ShowMessage('TabDraw');
  50. end;
  51.  
  52. procedure TForm1.Timer1Timer(Sender :TObject);
  53. begin
  54.   if Assigned(FOnTimer) then FOnTimer(Sender);
  55. end;
  56.  
  57. procedure TForm1.SetOnTimer(aValue :TEventProc);
  58. begin
  59.   if FOnTimer=aValue then Exit;
  60.   FOnTimer:=aValue;
  61. end;
  62.  
  63. ...
  64. ...
  65. Form1.Ontimer := @doSomething;
  66. ...
  67. ...
  68. end.
  69.  

or even soemthing like

Code: Delphi  [Select][+][-]
  1. type
  2.   TEventClass = class
  3.     DoSomething(aSender:TObject);
  4.   end;
  5. implementation
  6. var
  7.   EventCode : TEventClass;
  8.  
  9. procedure TEventClass.DoSomthing(aSender:TObject);
  10. begin
  11. //////......
  12. end;
  13. ...
  14. ...
  15. Timer1.Ontimer := @eventCode.DoSomething;
  16. initialization
  17.   EventCode := TEventClass.Create;
  18. finalization
  19.   EventCode.Free;
  20.  
  21.  
« Last Edit: January 30, 2016, 05:28:39 am by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: how to assign a regular procedure into an event handler?
« Reply #3 on: January 30, 2016, 09:29:34 am »
@Taazz: Yes you can ;) with this trick below:

Here's a technique that we use in KOL but also useful in general.
As long as you realize a procedure of object has a hidden self pointer you can set up a normal procedure to work as a procedure of object, provided you give it a dummy parameter.

Like this:
Code: Pascal  [Select][+][-]
  1. program testmeNow;
  2. {$APPTYPE CONSOLE}
  3. {$ifdef fpc}{$mode delphi}{$endif}
  4. type
  5.   TDoSomething = procedure (Sender: TObject) of object;
  6.  
  7.   TAtRunTime = class
  8.   private
  9.     FTest:TDoSomething;
  10.   public
  11.     property Test: TDoSomething read Ftest write FTest;
  12.   end;
  13.  
  14.  
  15.   procedure DoSomeThing(Dummy:Pointer;Sender:TObject);
  16.   begin
  17.     writeln('Hello');
  18.   end;
  19.  
  20.   function MakeMethod( Data, Code: Pointer ): TMethod;
  21.   begin
  22.     Result.Data := Data;
  23.     Result.Code := Code;
  24.   end;
  25.  
  26. var
  27.   A:TAtRuntime;
  28. begin
  29.   A := TAtRuntime.Create;
  30.   Try
  31.     A.Test := TDoSomething(MakeMethod(nil, @DoSomething));
  32.     A.Test(A);
  33.   finally
  34.     A.Free;
  35.   end;
  36.   readln;
  37. end.
  38.  

As you can see, you can also assign this to a TNotifyEvent property (same here) . Which is basically what you wanted.
« Last Edit: January 30, 2016, 09:50:07 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: how to assign a regular procedure into an event handler?
« Reply #4 on: January 30, 2016, 10:30:04 am »
It is also possible to setup the call stack with some assembler to accept a dummy parameter before calling out to the normal procedure (without the dummy), but that is not portable. The above example is.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: how to assign a regular procedure into an event handler?
« Reply #5 on: January 30, 2016, 10:41:41 am »
Hi all,

I got a little problem here. I'd like to assign a regular procedure into an event handler. Say I have a TForm with a TTimer. But, instead of assign the timer's event on design time, I want to assign it on run time with a regular procedure.

I've tried to do this:
Code: Pascal  [Select][+][-]
  1. ...
  2. procedure doSometing(Sender: TObject);
  3. begin
  4.   ...
  5. end;
  6.  
  7. Timer1.OnTimer := @doSomething;
  8. ...
For completeness:
When using my example code would thus become:
Code: Pascal  [Select][+][-]
  1. procedure doSometing(Dummy:Pointer;Sender: TObject);
  2. begin
  3.   //
  4. end;
  5.  
  6. Timer1.OnTimer := TNotifyEvent(MakeMethod(nil,@doSomething));
  7.  
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: how to assign a regular procedure into an event handler?
« Reply #6 on: January 30, 2016, 10:00:04 pm »
Another way :
Code: Pascal  [Select][+][-]
  1.  // Remeber that method subroutines have hidden Self parameter variable in addition to other variables.
  2. procedure doSometing(ASelf : TObject; Sender: TObject);
  3. begin
  4.   ...
  5. end;
  6.  
  7.  
  8. Var
  9.   ANotifyEvent : TNotifyEvent;
  10. begin
  11.   // TMethod is a record which have fields named Code and Data.
  12.   TMethod(ANotifyEvent).Code := @doSometing; // Address of the routine
  13.   TMethod(ANotifyEvent).Data := Pointer(Timer1); // Value of this field will be passed as an hidden Self parameter to method subroutine.
  14.   Timer1.OnTimer := ANotifyEvent;
  15. end;

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: how to assign a regular procedure into an event handler?
« Reply #7 on: January 31, 2016, 12:08:15 pm »
You have a point there. I forgot.
My "method" should best look like this: ::)
Code: Pascal  [Select][+][-]
  1. Timer1.OnTimer := TNotifyEvent(MakeMethod(Timer1,@doSomething));
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: how to assign a regular procedure into an event handler?
« Reply #8 on: February 02, 2016, 12:50:32 am »
I think you guys are taking it too far. This was a really good solution:
Code: Pascal  [Select][+][-]
  1.   TForm1 = class(TForm)
  2.     ...
  3.   public
  4.     procedure doSomething(Sender: TObject);
  5.   end;
  6. ...
  7. procedure TForm1.doSomething(Sender: TObject);
  8. begin
  9. end;
  10. ...
  11.   Timer1.OnTimer := @doSomething;
« Last Edit: February 02, 2016, 03:49:21 am by User137 »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: how to assign a regular procedure into an event handler?
« Reply #9 on: February 02, 2016, 02:24:12 am »
you guys are my heros ;) this thread is now bookmarked. Its the complete reference for this problem.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

 

TinyPortal © 2005-2018