【问题标题】:How to assign procedure from custom unit to event handler control on main form如何将自定义单元的过程分配给主窗体上的事件处理程序控件
【发布时间】:2016-08-05 12:16:33
【问题描述】:

这是真的吗? 例如,我有form1 和button1。我只能在设计时分配给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 吗? 谢谢。

好的。单元1

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

单元2

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)。我还需要做什么?

【问题讨论】:

  • 不,有两个原因。 1)它不是一个TNotifyEvent,它是一个对象的过程(它是一个类的成员)。 2) Object Inspector 不知道它的存在,因为它不是一个正确定义(和发布)的过程。

标签: delphi eventhandler


【解决方案1】:

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

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

但是,您可以在运行时分配createArrTable(),并进行一些小的调整。

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

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

但是,如果您将 显式 Self 参数添加到 createArrTable(),则可以避免该问题,并跳过类对象要求:

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

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

但是,由于createArrTable() 仍然不是类的成员,因此您无法直接将其分配给OnClick 事件,但您可以使用TMethod 变量来方便分配,例如:

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

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

我应该在unit2 中更改什么,我可以将ButtonClick 分配给myButton1.OnClick

什么都没有。它已经是一个合适的事件处理程序。您所要做的就是将unit2 添加到unit1 中interface 部分的uses 子句,这样IDE 就可以在设计时看到ButtonClick(),然后您就可以分配Form2.ButtonClick到myButton1.OnClick。只知道您必须设置您的项目以在运行时创建Form2 之前 Form1,否则当 DFM 流系统尝试通过无效访问 ButtonClick 时,您可能会崩溃代码分配Form1.myButton.OnClick 的值时的Form2 对象。

为避免更改表单的创建顺序,您可以将事件处理程序放入单独的TDataModule,然后将其方法链接到表单上的事件。只要确保TDataModule 是在运行时创建的在创建表单之前:

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

【讨论】:

  • 虽然你可以这样做,但你不应该这样做。
  • 当然,有更好的方法来实现这一点。我只是说它在技术上是可行的。它有时会派上用场。
  • @RemyLebeau 谢谢!我会在星期一试试
  • @RemyLebeau 嗨,再次!请看第1个帖子。我用 tdatamodule 添加了信息。它不起作用(
【解决方案2】:

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

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

【讨论】:

  • 是的... createArrTable 在主单元中并分配给 onClick。今天我开始隔离这个和其他程序来分离单元(its general procedure for many controls) and stuck with this. Just wanted to decrease lines in main unit. And I thought abount "make the procedure be a method of another class" but didnt 尝试一下。不过,感谢您的快速回复!
  • 虽然@davidheffernan 是对的,但像往常一样,我个人会将发件人留在那里,它允许多个按钮调用相同的函数/过程,您可以确定它是哪个。未来的灵活性。
  • @admiral 一旦代码与 UI 分离,它就不应该知道这些信息。该信息应在 UI 级别进行处理。
  • @admiral 此外,您可以控制代码。如果您需要稍后添加参数,请稍后再做。现在添加未使用的参数以防您以后使用它们是不好的做法。
  • @davidHeffernan 我屈服于你的经验和知识,完全同意。然而,我自己的经验告诉我,我有 95% 的机会忘记了一些东西,以后会通过重用代码进行优化,并且经常希望我在那里留下一个简单的发件人。不过你是对的,这是草率的编码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 2015-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多