AgustinOrtu
22-12-2015, 02:54:50
A medida que mas se empiezan a usar los metodos anonimos, te entran ganas de hacer cosas como esta:
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := procedure(Sender: TObject)
begin
ShowMessage(Sender.ClassName);
end);
end;
Pero no se puede, el IDE reporta el error:
[DCC Error] Unit1.pas(51): E2009 Incompatible types: 'method pointer and regular procedure'
Navegando por la web, el gran David Heffernan (http://stackoverflow.com/users/505088/david-heffernan) tiene una solucion muy elegante, la cual se puede ver en este enlace (http://stackoverflow.com/questions/11491593/tproctobject-to-tnotifyevent)
Sin mas, me he tomado la molestia de extenderla un poco para soportar distintos tipos de eventos; He dividido la funcionalidad en dos unidades, una bastante "aburrida" en la que hay que declarar la misma clase una y otra vez pero agregando siempre un parametro generico mas, asi:
TEventWrapper< T1, R > = class abstract(TComponent)
strict private
FProc: TProc< T1 >;
public
class function CreateEvent(AOwner: TComponent; AProc: TProc<T1>): R; virtual; abstract;
constructor Create(AOwner: TComponent; AProc: TProc<T1>); reintroduce;
procedure Event(Arg1: T1);
end;
TEventWrapper< T1, T2, R > = class abstract(TComponent)
strict private
FProc: TProc< T1, T2 >;
public
class function CreateEvent(AOwner: TComponent; AProc: TProc< T1, T2 >): R; virtual; abstract;
constructor Create(AOwner: TComponent; AProc: TProc< T1, T2 >); reintroduce;
procedure Event(Arg1: T1; Arg2: T2);
end;
TEventWrapper< T1, T2, T3, R > = class abstract(TComponent)
...
Y en otra unidad la implementacion para los eventos, seria basicamente implementar la funcion de clase CreateEvent
Esto permitiria escribir codigo de la siguiente manera:
uses
Events.Wrappers;
procedure ButtonOnClickEvent(Sender: TObject);
begin
ShowMessageFmt('%s - %s', [Sender.ClassName, TButton(Sender).Caption]);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
AEvent: TProc<TObject>;
begin
Button1.OnClick := TNotifyEventWrapper.CreateEvent(Self,
procedure(Sender: TObject)
begin
ShowMessageFmt('%s - %s', [Sender.ClassName, TButton(Sender).Caption]);
end);
AEvent := ButtonOnClickEvent;
Button2.OnClick := TNotifyEventWrapper.CreateEvent(Self, AEvent);
OnMouseDown := TMouseDownEventWrapper.CreateEvent(Self,
procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer)
begin
ShowMessageFmt('Clicked on %d:%d', [X, Y]);
end);
end;
Las unidades las pueden obtener en un repo en GitHub que he creado ahora y que (espero) pueda ir actualizando con cosas similares (clases, funciones, ejemplos, etc)
Enlace a repositorio (https://github.com/ortuagustin/Delphi-Utils) o acá (https://github.com/ortuagustin/Delphi-Utils/tree/master/RTL) estan las dos unidades de las que hablo: Events.Core y Events.Wrappers
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := procedure(Sender: TObject)
begin
ShowMessage(Sender.ClassName);
end);
end;
Pero no se puede, el IDE reporta el error:
[DCC Error] Unit1.pas(51): E2009 Incompatible types: 'method pointer and regular procedure'
Navegando por la web, el gran David Heffernan (http://stackoverflow.com/users/505088/david-heffernan) tiene una solucion muy elegante, la cual se puede ver en este enlace (http://stackoverflow.com/questions/11491593/tproctobject-to-tnotifyevent)
Sin mas, me he tomado la molestia de extenderla un poco para soportar distintos tipos de eventos; He dividido la funcionalidad en dos unidades, una bastante "aburrida" en la que hay que declarar la misma clase una y otra vez pero agregando siempre un parametro generico mas, asi:
TEventWrapper< T1, R > = class abstract(TComponent)
strict private
FProc: TProc< T1 >;
public
class function CreateEvent(AOwner: TComponent; AProc: TProc<T1>): R; virtual; abstract;
constructor Create(AOwner: TComponent; AProc: TProc<T1>); reintroduce;
procedure Event(Arg1: T1);
end;
TEventWrapper< T1, T2, R > = class abstract(TComponent)
strict private
FProc: TProc< T1, T2 >;
public
class function CreateEvent(AOwner: TComponent; AProc: TProc< T1, T2 >): R; virtual; abstract;
constructor Create(AOwner: TComponent; AProc: TProc< T1, T2 >); reintroduce;
procedure Event(Arg1: T1; Arg2: T2);
end;
TEventWrapper< T1, T2, T3, R > = class abstract(TComponent)
...
Y en otra unidad la implementacion para los eventos, seria basicamente implementar la funcion de clase CreateEvent
Esto permitiria escribir codigo de la siguiente manera:
uses
Events.Wrappers;
procedure ButtonOnClickEvent(Sender: TObject);
begin
ShowMessageFmt('%s - %s', [Sender.ClassName, TButton(Sender).Caption]);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
AEvent: TProc<TObject>;
begin
Button1.OnClick := TNotifyEventWrapper.CreateEvent(Self,
procedure(Sender: TObject)
begin
ShowMessageFmt('%s - %s', [Sender.ClassName, TButton(Sender).Caption]);
end);
AEvent := ButtonOnClickEvent;
Button2.OnClick := TNotifyEventWrapper.CreateEvent(Self, AEvent);
OnMouseDown := TMouseDownEventWrapper.CreateEvent(Self,
procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer)
begin
ShowMessageFmt('Clicked on %d:%d', [X, Y]);
end);
end;
Las unidades las pueden obtener en un repo en GitHub que he creado ahora y que (espero) pueda ir actualizando con cosas similares (clases, funciones, ejemplos, etc)
Enlace a repositorio (https://github.com/ortuagustin/Delphi-Utils) o acá (https://github.com/ortuagustin/Delphi-Utils/tree/master/RTL) estan las dos unidades de las que hablo: Events.Core y Events.Wrappers