自定义按钮OnClick事件

问题描述:

我在运行时创建按钮,并希望能够自定义设置OnClick事件,单击时每个按钮将通过它自己的自定义值:自定义按钮OnClick事件

试图assiign定制的OnClick过程时,我得到错误:

E2010 Incompatible types: 'TNotifyEvent' and 'procedure, untyped pointer or untyped parameter' 

这是我如何使用它:

procedure myOnClick(Sender:TObject; Info1:string; info2:integer); 
begin 
    // process info1, info2 based on which button is Sender 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var vBtn:Tbutton; 
    Idx:integer; 
    vStr:string; 
begin 
    // ... 

    // create btn 
    vBtn := Tbutton.Create(nil); 
    vBtn.Parent := Form1.ButtonsPanel; 
    vBtn.Tag := Idx; 
    vBtn.OnClick:=myOnCLick(self,vStr,vBtn.Tag); 

    // assign btn 
    //... 

end; 

如何使用自定义点击事件,过程,所以我可以通过值SPECI按下按钮时,点击按钮?


编辑:

现在我用的是CustomOnClick:

procedure TForm1.CustomOnClick(Sender: TObject); 
begin 
    myOnClick(Sender,TControl(Sender).Tag); 
end; 

vBtn.OnClick:=Form1.CustomOnClick; // works, but only accepts Sender parameter 

当大卫的建议,我已经使用Tag属性。现在我需要更多信息,并希望摆脱这个'中间人'CustomOnClick并直接调用myOnClick。

+2

你不会补偿t他对事件的签名。他们传递给你,用石头写成。使用“标签”来保存特定于按钮的信息。在那里放一个指向记录的指针。或者是对类实例的引用。或TButton的子类,并将信息放在那里作为字段。后一种方法在一定程度上是首选。 –

+1

或者创建一个vStr值的数组,并使用.'Tag'作为数组的索引。如果您需要更多信息,请使用记录阵列或对象列表等。 –

+2

标签可以容纳一个指针,以便您可以在其中放入任何您喜欢的信息。 –

方法1

您可以存储 “按钮信息” separatelly,像有:

type 
    TButtonInfo = record 
    info1: string; 
    info2: Integer; 
    info3: Double; 

    constructor Create(const AInfo1: string; AInfo2: integer; AInfo3: Double); 
    end; 

    TButtonInfoDictionary = class(TDictionary<TObject,TButtonInfo>) 
    end; 

    TForm6 = class(TForm) 
    ..... 
    private 
    { Private declarations } 
    FButtonInfoDict: TButtonInfoDictionary; 
    procedure OnButtonClick(Sender: TObject); 
    public 
    { Public declarations } 
    destructor Destroy; override; 
    end; 

implementation 

constructor TButtonInfo.Create(const AInfo1: string; AInfo2: integer; 
    AInfo3: Double); 
begin 
    Info1:=AInfo1; 
    Info2:=AInfo2; 
    Info3:=AInfo3; 
end; 

procedure TForm6.btnAddNewButtonClick(Sender: TObject); 
var 
    btn: TButton; 
begin 
    if not Assigned(FButtonInfoDict) then 
    FButtonInfoDict:=TButtonInfoDictionary.Create; 

    btn:=TButton.Create(nil); 
    btn.Parent:=Self; 
    btn.Align:=alTop; 
    btn.Caption:='btn'+FButtonInfoDict.Count.ToString; 
    btn.OnClick:=OnButtonClick; 

    FButtonInfoDict.Add(btn, TButtonInfo.Create(FButtonInfoDict.Count.ToString, FButtonInfoDict.Count, 0)); 
end; 

destructor TForm6.Destroy; 
begin 
    FreeAndNil(FButtonInfoDict); 
    inherited; 
end; 

procedure TForm6.OnButtonClick(Sender: TObject); 
var 
    ButtonInfo: TButtonInfo; 
begin 
    if Assigned(FButtonInfoDict) then 
    if FButtonInfoDict.TryGetValue(Sender, ButtonInfo) then 
     Caption:=ButtonInfo.Info1+' ' + ButtonInfo.info2.ToString; 
end; 

方法2正如大卫说,更优选的。

“展开” TButton的类和需要的属性添加到新的类:

type 
    TButton = class(Vcl.StdCtrls.TButton) 
    private 
    FInfo2: integer; 
    FInfo1: string; 
    public 
    property Info1: string read FInfo1 write FInfo1; 
    property Info2: integer read FInfo2 write FInfo2; 
    end; 

    TForm6 = class(TForm) 
    ..... 
    private 
    { Private declarations } 
    procedure OnButtonClick(Sender: TObject); 
    public 
    { Public declarations } 
    end; 

procedure TForm6.btnAddNewButtonClick(Sender: TObject); 
var 
    btn: TButton; 
begin 
    btn:=TButton.Create(nil); 
    btn.Parent:=Self; 
    btn.Align:=alTop; 
    btn.Caption:='btn'+Self.Tag.ToString; 
    btn.Info1:=Self.Tag.ToString; 
    btn.Info2:=Self.Tag; 
    btn.OnClick:=OnButtonClick; 

    Self.Tag:=Self.Tag + 1; 
end; 

procedure TForm6.OnButtonClick(Sender: TObject); 
begin 
    if Sender is TButton then 
    Caption:=TButton(Sender).Info1+' ' + TButton(Sender).info2.ToString; 
end; 

方法3,不需要

使用动态分配的店面TButtonInfo从方法1内存:

PButtonInfo = ^TButtonInfo; 

procedure TForm6.btnAddNewButtonClick(Sender: TObject); 
var 
    btn: TButton; 
    pInfo: PButtonInfo; 
begin 
    btn:=TButton.Create(nil); 
    btn.Parent:=Self; 
    btn.Align:=alTop; 
    btn.Caption:='btn'+Self.Tag.ToString; 
    btn.OnClick:=OnButtonClick; 

    New(pInfo); // once we have allocated memory, we need to release it after all. 
    // but we dont have event, where we can call Dispose(PButtonInfo(btn.tag)); 
    // so, we have memory leak... 
    // You can change TButtonInfo declaration from record to class, 
    // and use .Create against New, but memory leak still there 
    pInfo.info1:=Self.Tag.ToString; 
    pInfo.info2:=Self.Tag; 
    btn.Tag:=NativeInt(pInfo); 

    Self.Tag:=Self.Tag + 1; 
end; 

procedure TForm6.OnButtonClick(Sender: TObject); 
begin 
    if Sender is TButton then 
    Caption:=PButtonInfo(TButton(Sender).Tag).Info1+' ' + PButtonInfo(TButton(Sender).Tag).info2.ToString; 
end; 
+0

太棒了!我认为方法2最适合用我的代码实现,让我看看。 –

+0

'OnButtonClick'可以以不同的方式定义,而不是TForm(例子#2)?我想尽可能地将这些按钮放在单独的单元中 - 没有形式。这可能吗? –

+1

@MikeTorrettinni,是的。但只作为任何类别的任何对象的方法,而不是正常的程序。你可以做'btn.OnClick:= myNewObjectOfSuperClass.ButtonClick' – kami