如何将过程从自定义单元分配给主窗体上的事件处理程序控件
How to assign procedure from custom unit to event handler control on main form
这是真的吗?
例如,我有 form1
和 button1
。我只能在设计时将分配给button1.onClick
Button26Click
。
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
添加到 unit1
中 interface
部分的 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)
这是真的吗?
例如,我有 form1
和 button1
。我只能在设计时将分配给button1.onClick
Button26Click
。
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
tobutton1.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 assignButtonClick
tomyButton1.OnClick
没有。它已经是一个合适的事件处理程序。您所要做的就是将 unit2
添加到 unit1
中 interface
部分的 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)