Recent

Author Topic: Implementing custom events  (Read 3886 times)

simsee

  • Full Member
  • ***
  • Posts: 230
Implementing custom events
« on: August 19, 2025, 09:06:38 pm »
I'm writing a component that needs to handle custom events. To be consistent with the LCL philosophy, should the handler's trigger be placed at the beginning or end of the procedure that generates the event? I'll try to explain with a meaningless example, which only helps me clarify the question:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. uses
  3.   Classes;
  4. type
  5.  
  6.   { TMyComponent }
  7.  
  8.   TMyComponent=class(TComponent)
  9.     private
  10.       fOnEvent : TNotifyEvent;
  11.     protected
  12.       procedure DoEvent(Sender : TObject);
  13.     published
  14.       property OnEvent : TNotifyEvent read fOnEvent write fOnEvent;
  15.     public
  16.       procedure Event;
  17.   end;
  18.  
  19. { TMyComponent }
  20.  
  21. procedure TMyComponent.DoEvent(Sender: TObject);
  22. begin
  23.   if Assigned(fOnEvent) then
  24.     DoEvent(Sender);
  25. end;
  26.  
  27. procedure TMyComponent.Event;
  28. var
  29.   Obj : TObject;
  30. begin
  31.   DoEvent(Obj);  //call at the beginning?
  32.   writeln(1);
  33.   writeln(2);
  34.   writeln(3);
  35.   DoEvent(Obj); //or call at the end?
  36. end;
  37.  
  38. begin
  39. end.  

Thank you.

Zvoni

  • Hero Member
  • *****
  • Posts: 3230
Re: Implementing custom events
« Reply #1 on: August 20, 2025, 08:49:54 am »
This looks wrong:
Code: Pascal  [Select][+][-]
  1. procedure TMyComponent.DoEvent(Sender: TObject);
  2. begin
  3.   if Assigned(fOnEvent) then
  4.     DoEvent(Sender); //<-- HERE!! Calls infinite recursion
  5. end;

Would have expected:
Code: Pascal  [Select][+][-]
  1. procedure TMyComponent.DoEvent(Sender: TObject);
  2. begin
  3.   if Assigned(fOnEvent) then
  4.     fOnEvent(Sender);  //<-- HERE!
  5. end;

As for your Question:
It basically boils down to the "Before/After"-Events you know from everywhere.
An Event notifies the User that something happens.
Do you want to notify him/her BEFORE something happens afterwards ("Are you sure you want to delete?"), or do you want to notify him/her AFTER something has happened ("Copying files has finished!")?
Remember: Calling an Event is STILL like calling any other procedure
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

simone

  • Hero Member
  • *****
  • Posts: 687
Re: Implementing custom events
« Reply #2 on: August 20, 2025, 09:36:44 am »
This looks wrong:
Code: Pascal  [Select][+][-]
  1. procedure TMyComponent.DoEvent(Sender: TObject);
  2. begin
  3.   if Assigned(fOnEvent) then
  4.     DoEvent(Sender); //<-- HERE!! Calls infinite recursion
  5. end;

Would have expected:
Code: Pascal  [Select][+][-]
  1. procedure TMyComponent.DoEvent(Sender: TObject);
  2. begin
  3.   if Assigned(fOnEvent) then
  4.     fOnEvent(Sender);  //<-- HERE!
  5. end;

Thanks, Zvoni. Yes, you're right. I wrote the code with my smartphone. It was a typo.

As for your Question:
It basically boils down to the "Before/After"-Events you know from everywhere.
An Event notifies the User that something happens.
Do you want to notify him/her BEFORE something happens afterwards ("Are you sure you want to delete?"), or do you want to notify him/her AFTER something has happened ("Copying files has finished!")?
Remember: Calling an Event is STILL like calling any other procedure

Yes, that's clear to me. My question was to understand what the typical approach is in LCL components for standard events, in order to be consistent.
Microsoft Windows 10/11 64 bit - Lazarus 3.8/4.0 FPC 3.2.2 x86_64-win64-win32/win64

Zvoni

  • Hero Member
  • *****
  • Posts: 3230
Re: Implementing custom events
« Reply #3 on: August 20, 2025, 09:48:51 am »
Yes, that's clear to me. My question was to understand what the typical approach is in LCL components for standard events, in order to be consistent.
Ahhh....OK
As far as i understand it, if there are no explicit Before/After-Events (see above), it's when you expect it in the logical order.

Think about the Events of a TForm (OnCreate, OnShow, OnActivate etc.) and their order of appearance.

Without having looked into the source:
The TForm-Constructor "create" executes code first, then Calls OnCreate (if Assigned), then after optionally executing the OnCreate-Code it returns to the calling point, executes further code (depending on what happened in OnCreate), paints the Form then calls OnShow (if assigned), returns back to the calling point, executes further code, then calls OnActivate (if assigned) and so on and so on.....

So you have to decide when it makes sense to call the Event.
Do you need some variables initialized with special values before you call the event etc. etc...
« Last Edit: August 20, 2025, 10:22:52 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

simsee

  • Full Member
  • ***
  • Posts: 230
Re: Implementing custom events
« Reply #4 on: August 20, 2025, 12:02:57 pm »
Thanks, Zvoni.

Of course, in some cases, the trigger point depends on the type of operation. Other times, it can be useful to place it at both the beginning and the end.

In fact, some components (third-party ones, not LCL ones, I mean) have events like OnBeforeXXX and OnAfterXXX, where XXX is the operation.

Perhaps I should inspect the code to understand the LCL approach. For example, in the case of an OnMouseDown event, to see if the trigger occurs before the mouse button is pressed or after it rises.

Zvoni

  • Hero Member
  • *****
  • Posts: 3230
Re: Implementing custom events
« Reply #5 on: August 20, 2025, 01:09:14 pm »
Perhaps I should inspect the code to understand the LCL approach. For example, in the case of an OnMouseDown event, to see if the trigger occurs before the mouse button is pressed or after it rises.
Think about it:
1) If OnMouseDown fires when it RISES UP, why would there be an OnMouseUp?
2) How can the Event be fired BEFORE the Mouse-Button is pressed? There is no signal "Mouse button pressed" coming from the OS

OnMouseDown fires when the Button is pressed (Receiving the Signal from the OS), in that context: AFTER being pressed, but before rising up/being released.
Further think about how you would implement Drag&Drop-operations, where you keep the Mouse-Button pressed.
It's the same with KeyDown/KeyUp: same principle.

it boils down to: When does it make (logical) sense to fire an event?
It would confuse the people if you fire a MouseDown-Event after it rises up again....
« Last Edit: August 20, 2025, 01:13:32 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

simsee

  • Full Member
  • ***
  • Posts: 230
Re: Implementing custom events
« Reply #6 on: August 20, 2025, 01:43:39 pm »
Thanks for these thoughts. Perhaps I used an insignificant example.

In my case, the component I created includes an OnAfterMouseUp, as the program that uses it needs to perform some operations AFTER the mouse button has returned in the up position.

Hence my initial question.

Zvoni

  • Hero Member
  • *****
  • Posts: 3230
Re: Implementing custom events
« Reply #7 on: August 20, 2025, 01:59:21 pm »
Thanks for these thoughts. Perhaps I used an insignificant example.

In my case, the component I created includes an OnAfterMouseUp, as the program that uses it needs to perform some operations AFTER the mouse button has returned in the up position.

Hence my initial question.
If it needs to perform operations AFTER the Mouse goes up, IRRESPECTIVE if an Event-Handler is assigned (That's important!!), you fire the Event at the beginning of your procedure
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1570
    • Lebeau Software
Re: Implementing custom events
« Reply #8 on: August 20, 2025, 06:08:56 pm »
should the handler's trigger be placed at the beginning or end of the procedure that generates the event?

That is entirely up to you to decide based on your component's needs.  There is no standard convention for that.

Code: Pascal  [Select][+][-]
  1. procedure TMyComponent.DoEvent(Sender: TObject);
  2. begin
  3.   if Assigned(fOnEvent) then
  4.     DoEvent(Sender);
  5. end;

As stated earlier, DoEvent() needs to call fOnEvent instead of DoEvent recursively. However, it should also be passing its Self pointer instead of a caller-specified Sender, eg:

Code: Pascal  [Select][+][-]
  1. procedure TMyComponent.DoEvent;
  2. begin
  3.   if Assigned(fOnEvent) then
  4.     fOnEvent(Self);
  5. end;
  6.  
  7. procedure TMyComponent.Event;
  8. begin
  9.   ...
  10.   DoEvent;
  11.   ...
  12. end;
  13.  

DoEvent() should take a Sender parameter only when it is itself an event handler of another component. But even then, the Sender of your component's events should always be your component itself.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018