Further to this post whose accepted answer remains very cryptic:
@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) begin ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' end )^ ) + $0C)^;
I wonder wether it is possible to devise a simplest and elegant way akin to:
Button.OnClick :=
AnonProc2NotifyEvent (
procedure (Sender: TObject)
begin
((Sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!'
end
);
so as to attain the same purpose and where AnonProc2NotifyEvent is a method of the owner of Button with the following signature:
TOwnerOfButton = class(TForm)
Button: TButton;
...
private
...
protected
function AnonProc2NotifyEvent(aProc: TProc<TObject>): TNotifyEvent;
public
...
end;
Is that feasible and if so how to implement it ?
This will do the job readily enough:
type
TNotifyEventWrapper = class(TComponent)
private
FProc: TProc<TObject>;
public
constructor Create(Owner: TComponent; Proc: TProc<TObject>);
published
procedure Event(Sender: TObject);
end;
constructor TNotifyEventWrapper.Create(Owner: TComponent; Proc: TProc<TObject>);
begin
inherited Create(Owner);
FProc := Proc;
end;
procedure TNotifyEventWrapper.Event(Sender: TObject);
begin
FProc(Sender);
end;
function AnonProc2NotifyEvent(Owner: TComponent; Proc: TProc<TObject>): TNotifyEvent;
begin
Result := TNotifyEventWrapper.Create(Owner, Proc).Event;
end;
The Owner
parameter in AnonProc2NotifyEvent
is so that the lifetime of the wrapper object can be managed. Without something like that you would leak instances of TNotifyEventWrapper
.
Pass as Owner
, the component to which you are connecting the event. For example:
Button1.OnClick := AnonProc2NotifyEvent(
Button1,
procedure(Sender: TObject)
begin
(Sender as TButton).Caption := 'Clicked';
end
);
So, when the button is destroyed, the TNotifyEventWrapper
will also be destroyed. The wrapper object must live at least as long as the object to whose events it is associated. And so the choice of Button1
as the owner is the natural and obvious one.
For reference this what I am getting at, I studied Barry Kelly's blog post referenced in the prior SO post mentioned above and came up with this solution:
function TMainForm.Proc2NotifyEvent(const aProc: TNotifyReference): TNotifyEvent;
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
TMethod(Result).Code := PPVtable((@aProc)^)^^[3];
TMethod(Result).Data := Pointer((@aProc)^);
end;
Still cryptic but encapsuled, so easing the task of the coder compared to the initial method.
I tried to tidy MethRefToMethPtr and MakeNotify and put it all in one method.
Notice that there was (a slight) change in the method's signature, the argument aProc became const.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With