Delphi – Set onclick event at runtime in Delphi XE

delphievent handlingfiremonkey

First post here so please forgive any etiquette blunders.

I'm creating a multi-device (FMX) app in Delphi XE8 and am having difficulty assigning an event handler to a dynamically created button. I searched through StackOverflow and found answers relating to NotifyEvents so I followed the advice in those answers – still no luck. The compilation error is "E2010 Incompatible types: 'TNotifyEvent' and 'Procedure'".

I've put together a simple test case of a form with an edit field and a static Hello button, a 2nd button creates a Goodbye button and tries to assign a procedure to the OnClick event but I'm still getting the same error.

As far as I can tell I've followed all of the requirements of making the procedure compatible with a TNotifyEvent but even this basic example fails with the same error. I'm banging my head against the wall so can someone please let me know what I've done wrong?

Many thanks.

unit Dynamic_Button_Test1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, FMX.Edit;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Hello: TButton;
    Create_GoodBye: TButton;
    procedure HelloClick(Sender: TObject);
    procedure Create_GoodByeClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure GoodbyeClick(Sender: TObject) ;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Create_GoodByeClick(Sender: TObject);
var
  New_Button : TButton ;
begin
New_Button := TButton.Create( Form1 );
New_Button.Parent := Form1 ;
New_Button.Text := 'Goodbye' ;
New_Button.Visible := True ;
New_Button.Margins.Left := 50 ;
New_Button.Margins.Right := 50 ;
New_Button.Margins.Bottom := 30 ;
New_Button.Height := 50 ;
New_Button.Align := TAlignLayout.Bottom ;

New_Button.OnClick := TForm1.GoodbyeClick ;
end;

procedure TForm1.HelloClick(Sender: TObject);
begin
Edit1.Text := 'Hello' ;
end;

procedure TForm1.GoodbyeClick(Sender: TObject);
begin
Edit1.Text := 'Goodbye' ;
end;

end.

Best Answer

VCL/FMX event handlers are tied to specific objects at runtime. When assigning your event handler, you need to replace the class typename with an object pointer. That object will be the event handler's Self pointer when the event is triggered later on:

New_Button.OnClick := Self.GoodbyeClick ;

Or simply:

New_Button.OnClick := GoodbyeClick ; // Self implicitly used

On a side note - when creating the Button, that code is inside of a TForm1 instance method, so you should be using the Self object pointer instead of the global Form1 object pointer:

New_Button := TButton.Create( Self );
New_Button.Parent := Self ;