我在运行时创建按钮,并且希望能够设置自定义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
正如大卫所说,我已经使用了Tag属性。现在我需要更多的信息,并希望摆脱这个中间人' CustomOnClick并直接调用myOnClick。
答案 0 :(得分:3)
方法1
您可以存储"按钮信息"像那里一样分开:
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 正如大卫所说,更喜欢。
&#34;展开&#34; 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 ,不需要的
为方法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;