如何将过程从自定义单元分配给主窗体上的事件处理程序控件

How to assign procedure from custom unit to event handler control on main form

这是真的吗? 例如,我有 form1button1。我只能在设计时分配给button1.onClickButton26Click

  TForm1 = class(TForm)    
    procedure Button26Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

我也有第 2 单元:

unit ProcAndFunc;
interface
uses sysutils,forms;

procedure createArrTable(Sender: TObject);

我可以在设计时createArrTable分配给button1.onClick吗? 谢谢。

好的。第一单元

TForm1 = class(TForm)  
        myButton1 : TButton; 
        procedure Button26Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;

Unit2

TForm2 = class(TForm) 
            myButton2 : TButton; 
            procedure ButtonClick(Sender: TObject);
          private
            { Private declarations }
          public
            { Public declarations }
          end;

只是想知道它是如何工作的。我应该在 unit2 中更改什么,我可以将 ButtonClick 分配给 myButton1.OnClick How to make a separate unit for event methods, which IDE allows me to assign to component events at design time? 看来我重复了这个问题。但这个答案不适合我。还有别的办法吗?

EDIT3(08.08.2016): 我创建了这个 TDataModule;

unit Unit3;

interface

uses
  System.SysUtils, System.Classes,dialogs;

type
  TDataModule3 = class(TDataModule)
  procedure Click2(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DataModule3: TDataModule3;

implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

procedure TDataModule3.Click2(Sender: TObject);
begin
ShowMessage('hello world');
end;   

end.

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, unit3;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('hello world');
end;

end.

而且我仍然无法在设计时将 DataModule3.Click2 分配给 button1.onclick。 (screenshot)。我还需要做什么?

您永远不能将该过程指定为事件处理程序,因为它不是 class 的方法。因此,您可以使该过程成为另一个 class 的方法,并确保设计者可以看到该 class 的全局实例。

然而,在我看来,这将是一个非常糟糕的解决方案。最好的做法是尽可能将 UI 逻辑与应用程序逻辑分离。干净的解决方案是简单地从事件处理程序中调用该过程。这意味着您的程序仍然可以以其当前形式供其他方使用,并且不需要被硬塞到看起来像一个事件处理程序。您也可以删除 Sender 参数,我怀疑您在任何情况下都不会使用它。

Can I assign createArrTable to button1.onClick in design time?

不是在设计时,不是。 createArrTable() 不是 class 的成员,因此在 运行 时无法通过对象指针访问。因此它不满足 OnClick 事件处理程序通常所需的签名。 IDE 不允许您在设计时分配它。

但是,您 可以 在 运行 时分配 createArrTable(),稍作调整。

OnClick 事件(以及一般的大多数 RTL/VCL 事件)是使用 闭包 实现的,它由 2 个指针组成 - 指向过程代码本身,以及指向对象的指针。

当事件在 运行 时间触发时,编译代码从闭包中提取对象指针并调用该过程,该对象传递给过程的 Self 参数。缺少 Self 参数会阻止您当前的 createArrTable() 实现被用作 OnClick 事件处理程序。如果您以某种方式按原样分配它,参数列表将在 运行 时损坏。

但是,如果您向 createArrTable() 添加 explicit Self 参数,则可以避免该问题,并跳过 class 对象要求:

procedure createArrTable(Self: Pointer; Sender: TObject);

在调用过程时,按钮仍会尝试将对象指针从闭包传递到 Self,但现在您已经 space 正确传递对象指针而不损坏其他参数。对象指针本身变得无关紧要,可以设置为任何你想要的,甚至是 nil。

但是,由于 createArrTable() 仍然不是 class 的成员,您不能将其直接 分配给 OnClick 事件,但是你可以使用一个 TMethod 变量来方便赋值,例如:

var
  M: TMethod;
begin
  M.Code := @createArrTable;
  M.Data := WhateverYouWantSelfToBe; 
  Button1.OnClick := TNotifyEvent(M);
end;

现在 createArrTable() 将在 运行 时触发 OnClick 时按预期工作。

What should I change in unit2 that I can assign ButtonClick to myButton1.OnClick

没有。它已经是一个合适的事件处理程序。您所要做的就是将 unit2 添加到 unit1interface 部分的 uses 子句中,这样 IDE 就可以看到 ButtonClick()设计时,那么您就可以将 Form2.ButtonClick 分配给 myButton1.OnClick。只知道您必须将项目设置为在 Form2 之前 Form1 在 运行 时间创建,否则您可能会在DFM 流系统在分配 Form1.myButton.OnClick.

的值时尝试通过无效的 Form2 对象访问 ButtonClick

为避免必须更改表单的创建顺序,您可以将事件处理程序放入单独的 TDataModule 中,然后 link 将其方法添加到表单中的事件中。只需确保 TDataModule 在 运行 时间 创建表单之前创建:

Access DataModule's event from another Form (delphi design-time)