i have a method:
procedure Frob(Sender: TObject);
that i want to call when i click a menu item.
The method comes to me though an interface:
animal: IAnimal;
IAnimal = interface
procedure Frob(Sender: TObject);
end;
The question revolves around what to assign to the OnClick
event handler of a menu item (i.e. control):
var
animal: IAnimal;
...
begin
...
menuItem := TMenuItem.Create(FileMenu)
menuItem.Caption := 'Click me!';
menuItem.OnClick := <-------- what 开发者_运维技巧to do
...
end;
The obvious choice, my first attempt, and the wrong answer is:
menuItem.OnClick := animal.Frob;
So how can i call a method when user clicks a control?
See also
- Why doesn't it work?
Have whatever object you're in hold the animal in a private field, then set up a method that calls it. Like so:
procedure TMyClass.AnimalFrob(Sender: TObject);
begin
FAnimal.Frob(sender);
end;
Then the solution becomes easy:
menuItem.OnClick := self.AnimalFrob;
Another, slightly hackier approach would be to store a reference to the IAnimal
in the Tag
property of the TMenuItem.
This could be the index of the IAnimal in a TList as you suggested:
if Sender is TMenuItem then
IAnimal(FAnimals[TMenuItem(Sender).Tag]).Frob;
Or you could cast the interface to Integer.
MenuItem.Tag := Integer(AnAnimal);
Then cast back to IAnimal in the event handler:
if Sender is TMenuItem then
IAnimal(TMenuItem(Sender)).Frob;
This works well with Object references, some care may be required with interfaces due to the reference counting.
Note that Delphi 7 also has a TInterfaceList in Classes.pas
I know you tagged the question as answered, but here are some other suggestions :
type
IClicker = Interface
function GetOnClickProc : TNotifyEvent;
End;
type
TBlob = class( TInterfacedObject, IClicker )
procedure OnClick( Sender : TObject );
function GetOnClickProc : TNotifyEvent;
end;
{ TBlob }
function TBlob.GetOnClickProc : TNotifyEvent;
begin
Result := Self.OnClick;
end;
procedure TBlob.OnClick(Sender: TObject);
begin
MessageDlg('Clicked !', mtWarning, [mbOK], 0);
end;
{ MyForm }
var
clicker : IClicker;
begin
...
menuItem.OnClick := clicker.GetOnClickProc;
end;
Of course, you have to be careful about the lifetime of the "clicker" object...
If you can manipulate your objects as objects (and not only as interfaces), try adding a common subclass :
type
TClicker = class
procedure OnClick( Sender : TObject ); virtual;
end;
var
lClicker : TClicker;
...
menuItem.OnClick := lClicker.OnClick;
I would also go for Cosmin Prund's comment : make a specialized TMenuItem subclass.
精彩评论