自定义按钮 OnClick 事件
Custom Button OnClick event
我在运行时创建按钮,并希望能够设置自定义 OnClick 事件,每个按钮在单击时都会传递它自己的自定义值:
我在尝试分配自定义 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;
如何使用自定义点击事件、程序,以便在点击按钮时传递特定于按钮的值?
编辑:
现在我使用 CustomOnClick:
procedure TForm1.CustomOnClick(Sender: TObject);
begin
myOnClick(Sender,TControl(Sender).Tag);
end;
vBtn.OnClick:=Form1.CustomOnClick; // works, but only accepts Sender parameter
正如 David 所建议的,我已经使用了 Tag 属性。现在我需要更多信息并想摆脱这个 'middle man' CustomOnClick 并直接调用 myOnClick。
方法一
您可以单独存储 "button info",例如:
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;
方法二正如David所说,更优选。
"Expand" TButton class 并将需要的属性添加到新 class:
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,不需要
使用方法 1 中的动态分配内存存储 TButtonInfo:
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;
我在运行时创建按钮,并希望能够设置自定义 OnClick 事件,每个按钮在单击时都会传递它自己的自定义值:
我在尝试分配自定义 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;
如何使用自定义点击事件、程序,以便在点击按钮时传递特定于按钮的值?
编辑:
现在我使用 CustomOnClick:
procedure TForm1.CustomOnClick(Sender: TObject);
begin
myOnClick(Sender,TControl(Sender).Tag);
end;
vBtn.OnClick:=Form1.CustomOnClick; // works, but only accepts Sender parameter
正如 David 所建议的,我已经使用了 Tag 属性。现在我需要更多信息并想摆脱这个 'middle man' CustomOnClick 并直接调用 myOnClick。
方法一
您可以单独存储 "button info",例如:
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;
方法二正如David所说,更优选。
"Expand" TButton class 并将需要的属性添加到新 class:
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,不需要
使用方法 1 中的动态分配内存存储 TButtonInfo:
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;