Since anonymous methods appeared in Delphi I wanted to use them in VCL components events. Obviously for backward compatibility the VCL wasn't updated, so I managed to make a simple implementation with a few caveats.
type
TNotifyEventDispatcher = class(TComponent)
protected
FClosure: TProc<TObject>;
procedure OnNotifyEvent(Sender: TObject);
public
class function Create(Owner: TComponent; const Closure: TProc<TObject>): TNotifyEvent; overload;
function Attach(const Closure: TProc<TObject>): TNotifyEvent;
end;
implementation
class function TNotifyEventDispatcher.Create(Owner: TComponent; const Closure: TProc<TObject>): TNotifyEvent;
begin
Result := TNotifyEventDispatcher.Create(Owner).Attach(Closure)
end;
function TNotifyEventDispatcher.Attach(const Closure: TProc<TObject>): TNotifyEvent;
begin
FClosure := Closure;
Result := Self.OnNotifyEvent
end;
procedure TNotifyEventDispatcher.OnNotifyEvent(Sender: TObject);
begin
if Assigned(FClosure) then
FClosure(Sender)
end;
end.
And this is how it's used for example:
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := TNotifyEventDispatcher.Create(Self,
procedure (Sender: TObject)
begin
Self.Caption := 'DONE!'
end)
end;
Very simple I believe, there are two drawbacks:
I have to create a component to manage the lifetime of the anonymous method (I waste a bit more of memory, and it's a bit slower for the indirection, still I prefer more clear code in my applications)
I have to implement a new class (very simple) for every event signature. This one is a bit more complicated, still the VCL has very common event signatures, and for every special case when I create the class it's done forever.
What do you think of this implementation? Something to make it better?
You can take a look at my multicast event implementation in DSharp.
Then you can write code like this:
function NotifyEvent(Owner: TComponent; const Delegates: array of TProc<TObject>): TNotifyEvent; overload;
begin
Result := TEventHandler<TNotifyEvent>.Create<TProc<TObject>>(Owner, Delegates).Invoke;
end;
function NotifyEvent(Owner: TComponent; const Delegate: TProc<TObject>): TNotifyEvent; overload;
begin
Result := NotifyEvent(Owner, [Delegate]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := NotifyEvent(Button1, [
procedure(Sender: TObject)
begin
Caption := 'Started';
end,
procedure(Sender: TObject)
begin
if MessageDlg('Continue?', mtConfirmation, mbYesNo, 0) <> mrYes then
begin
Caption := 'Canceled';
Abort;
end;
end,
procedure(Sender: TObject)
begin
Caption := 'Finished';
end]);
end;
You could make TNotifyEventDispatcher
to be a subclass of TInterfacedObject
so you do not need to care about freeing it.
But to be more pragmatic one would use traditional event assignment that takes less lines of code and is supported by the IDE.
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