Delphi – 在运行时添加按钮的单击事件

huangapple go评论70阅读模式
英文:

Delphi - Adding onclick to a button created at runtime

问题

I understand that you want to set the onclick event of the buttons to call another procedure passing in the fname parameter. To achieve this, you can use an anonymous method (a feature introduced in recent versions of Delphi) to create a custom event handler. Here's how you can do it:

F1button.OnClick :=
  procedure(Sender: TObject)
  begin
    AnotherProcedure(fname);
  end;

By using an anonymous method, you can call AnotherProcedure(fname) when the button is clicked, passing the fname parameter as desired.

Make sure you have a recent version of Delphi that supports anonymous methods for this to work.

英文:

I have a procedure that creates a customtitlebar and titlebarpanel at runtime for any form name passed to it. The purpose of this is to handle the creation automatically for a bunch of different forms without having to worry about the current form control positions. This code shuffles all controls down automatically so they are not covered by the titlebarpanel.

This code works. Call this procedure from any form's oncreate adds 2 buttons to the form titlebar area.

procedure CreateTitleBarPanel(fname:tform);
var titlebarpanel:ttitlebarpanel;
    F1button,F12button:tspeedbutton;
    i:integer;
begin
     fname.customtitlebar.enabled:=true;
     titlebarpanel:=ttitlebarpanel.create(fname);
     titlebarpanel.parent:=fname;
     titlebarpanel.Visible:=true;
     fname.customtitlebar.control:=titlebarpanel;
     F1button:=tspeedbutton.create(titlebarpanel);
     F1button.parent:=titlebarpanel;
     F1button.top:=10;
     F1button.height:=fname.customtitlebar.height-15;
     F1button.width:=F1button.height;

	 //this is the problem
     F1button.onclick:=how??

     F12button:=tspeedbutton.create(titlebarpanel);
     F12button.parent:=titlebarpanel;
     F12button.top:=10;
     F12button.height:=fname.customtitlebar.height-15;
     F12button.width:=F12button.height;
     F12button.left:=fname.customtitlebar.clientrect.width - F1button.Width - 8;
     F1button.left:=F12button.left - F1button.Width - 8;
     //move all form components down to match the titlebar height
     for i:=0 to fname.ComponentCount-1 do
     begin
          try
             (fname.Components[i] as TControl).top:=(fname.Components[i] as TControl).top+fname.customtitlebar.height;
          except
                //control does not have a top property, so skip it
          end;
     end;
end;

My issue is that I want the onclick for the buttons to call another procedure passing in the fname, so eg something like

F1button.onclick:=AnotherProcedure(fname);

That doesn't work though. E2010 Incompatible types: 'TNotifyEvent' and 'procedure, untyped pointer or untyped parameter
Can anyone explain how I can get this to work? Button created at runtime, when clicked I want it to call AnotherProcedure(fname); (this procedure is the same unit as the CreateTitleBarPanel procedure if that helps.

Thanks for any help.

答案1

得分: 3

TSpeedButton.OnClick是一个事件处理程序(继承自Vcl.Controls.TControl.OnClick),其类型为TNotifyEvent

类型定义:

TNotifyEvent = procedure(Sender: TObject) of object;

这意味着您必须在一个对象上定义一个接受TObject参数的过程(可以是您的窗体或任何其他对象)。
例如:

TMyForm = class(TForm)
public
  procedure MyOnClick(Sender: TObject);
end;

...

procedure TMyForm.MyOnClick(Sender: TObject);
begin

end;

然后,您可以将它分配为您的按钮的OnClick事件处理程序,如下所示:

F1button.OnClick := MyOnClick;
英文:

TSpeedButton.OnClick is an event handler (inherited from Vcl.Controls.TControl.OnClick) and its type is TNotifyEvent.

Type definition:

TNotifyEvent = procedure(Sender: TObject) of object;

This means that you have to define a procedure accepting a TObject parameter on an object (it may be your form or any other object).
For example:

 TMyForm = class(TForm)
 public
   procedure MyOnClick(Sender : TObject);
 end;

...

procedure TMyForm.MyOnClick(Sender : TObject);
begin
  
end;

Then you'll be able to assign it as the OnClick event handler of your button in this way:

F1button.OnClick := MyOnClick;

答案2

得分: 1

无法按照您所写的方式进行操作。

按钮的OnClick事件是指向具有类型TNotifyEvent的过程的链接。这意味着首先,该过程必须只有一个类型为TObject的参数,其次,该过程必须是某个对象的方法。

如果您想要实现与您所写的类似的行为 - 您需要将此方法添加到您传递给CreateTitleBarPanel()的每个窗体中,并将其分配给您的按钮,或者您需要具有具有指定行为的全局对象的方法。类似于以下内容:

type
  TMyButtonsClickHandler = class(TObject)
  public
    procedure DoButtonClick(Sender: TObject);
  end;

var
  MyButtonsClickHandler: TMyButtonsClickHandler;

implementation

{ TMyButtonsClickHandler }

procedure TMyButtonsClickHandler.DoButtonClick(Sender: TObject);
var
  fname: TCustomForm;
begin
  // Sender必须是我们的其中一个按钮,请确保
  if Sender is TSpeedButton then
  begin
    // 获取我们按钮的窗体
    fname := GetParentForm(TSpeedButton(Sender), true);
    // 以表单对象作为输入参数调用您的功能,正如您所要求的
    AnotherProcedure(fname);
  end;
end;

initialization
  // 在任何调用之前创建我们的全局对象
  MyButtonsClickHandler := TMyButtonsClickHandler.Create;

finalization
  // 在结束时销毁我们的全局对象
  FreeAndNil(MyButtonsClickHandler);

end.

当您拥有了这个全局对象后,您可以在CreateTitleBarPanel()函数中使用F1button.OnClick := MyButtonsClickHandler.DoButtonClick;,然后单击任何这些按钮将运行您的AnotherProcedure(),并将父窗体作为参数传递。

英文:

You can't do it exactly as you wrote.

A button's OnClick event is a link to a procedure with type TNotifyEvent. That means that first of all this procedure must have only one parameter with type TObject, and second this procedure must be a method of some object.

If you want to have similar behavior as you wrote - you need to add this method to every form that you pass into CreateTitleBarPanel() and assign it to your buttons, or you need to have some global object with your method with specified behavior. Something like that:

type
  TMyButtonsClickHandler = class(TObject)
  public
    procedure DoButtonClick(Sender : TObject);
  end;

var      
  MyButtonsClickHandler : TMyButtonsClickHandler;

implementation  

{ TMyButtonsClickHandler }

procedure TMyButtonsClickHandler.DoButtonClick(Sender: TObject);
var
  fname : TCustomForm;
begin
  //Sender must by one of our buttons, make sure that
  if Sender is TSpeedButton then
  begin
    //get form of our button
    fname := GetParentForm(TSpeedButton(Sender), true);
    //calling your functionality with form object as IN param, as you ask
    AnotherProcedure(fname);
  end;
end;

initialization
  //create our global object before any calls
  MyButtonsClickHandler := TMyButtonsClickHandler.Create;

finalization
  //destroy our global object in finalization
  FreeAndNil(MyButtonsClickHandler);

end.

When you will have this global object, you can make F1button.OnClick :=
MyButtonsClickHandler.DoButtonClick;
inside your CreateTitleBarPanel() function, and then click on any of these buttons will run your AnotherProcedure() with the parent form as the parameter.

huangapple
  • 本文由 发表于 2023年5月10日 19:27:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76217846.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定