我想测试一些东西,并创建了一个简单的动态弹出菜单示例。我必须意识到弹出菜单可以正常显示,但它不会调用onClick事件处理程序。我试图重命名所有内容以避免名称冲突,将事件处理程序设为虚拟,公共,但是并不能解决问题。我已经重新启动了IDE(我认为这段代码应该可以正常工作),但它是相同的。编译器选项是新的项目默认值。静态创建的(放在窗体上)popupmenu可以很好地工作,只是动态创建的一个可以休息一下。 我应该用一些值填充动态菜单的哪个属性?请有人解释原因!
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Menus;
type
TForm1 = class(TForm)
Button1: TButton;
staticPopupMenu: TPopupMenu;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
procedure addMenuItem( popupmenu_ : TPopupMenu; caption_ : string; tag_ : integer; onClick_ : TNotifyEvent );
procedure onmenuitemclick1( sender_ : TObject );
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.addMenuItem( popupmenu_ : TPopupMenu; caption_ : string; tag_ : integer; onClick_ : TNotifyEvent );
var
menuitem1 : tmenuitem;
begin
menuitem1 := tmenuitem.create( popupmenu_ );
menuitem1.caption := caption_;
menuitem1.Tag := tag_;
menuitem1.onclick := onclick_;
popupmenu_.items.add( menuitem1 );
end;
procedure TForm1.onmenuitemclick1( sender_ : TObject );
var
id : integer;
begin
id := tmenuitem( sender_ ).Tag;
showmessage( 'menuitem.onclick called! (' + intToStr( id ) + ')' );
end;
procedure TForm1.Button1Click(Sender: TObject);
var
dynamicPopupMenu : tpopupmenu;
begin
dynamicPopupMenu := tpopupmenu.create( self );
try
addMenuItem( dynamicPopupMenu, 'aaa', 1, onmenuitemclick1 );
addMenuItem( dynamicPopupMenu, 'bbb', 2, onmenuitemclick1 );
dynamicPopupMenu.popup( 500, 500 );
finally
dynamicPopupMenu.Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
staticPopupMenu.items.Clear;
addMenuItem( staticPopupMenu, 'aaa', 1, onmenuitemclick1 );
addMenuItem( staticPopupMenu, 'bbb', 2, onmenuitemclick1 );
staticPopupMenu.popup( 500, 500 );
end;
end.
dfm:
object Form1: TForm1
Left = 339
Top = 270
Caption = 'Form1'
ClientHeight = 601
ClientWidth = 854
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 96
Top = 128
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 177
Top = 128
Width = 75
Height = 25
Caption = 'Button2'
TabOrder = 1
OnClick = Button2Click
end
object staticPopupMenu: TPopupMenu
Left = 280
Top = 128
end
end
答案 0 :(得分:2)
由于您要使用当前的dynamicPopupMenu
实例创建TForm1
与Owner
一样,我不确定自己是否需要垃圾收集器的所有功能,
因为TForm1
实例本身销毁后仍会销毁。
尝试一下:
在Classs.Pas中的TComponent.Destroy
上设置断点,并在Tag
上设置手表。
如下所示更改您的Button1Click
,禁用垃圾收集器,
编译,运行和观察。
{code}
procedure TForm1.Button1Click(Sender: TObject);
var
dynamicPopupMenu : tpopupmenu;
AForm : TForm;
begin
AForm := TForm.Create(Nil);
dynamicPopupMenu := tpopupmenu.create(AForm);
try
dynamicPOpUpMenu.Tag := 666;
addMenuItem( dynamicPopupMenu, 'aaa', 1, onmenuitemclick1 );
addMenuItem( dynamicPopupMenu, 'bbb', 2, onmenuitemclick1 );
dynamicPopupMenu.popup( 600, 600 );
finally
AForm.Release;
end;
end;
答案 1 :(得分:2)
这是我的看法。用户定义的消息应该可以很好地完成,并且开销很小。所以...
定义Windows消息:
const
WM_FREE_MY_DYNAMENU = WM_USER + 0;
将dynamicPopupMenu
变量移至表单并为消息定义处理程序:
TForm12 = class(TForm)
...
protected
dynamicPopupMenu: TPopupMenu;
procedure FreeMyDynaMenu(var Message: TMessage); message WM_FREE_MY_DYNAMENU;
实施:
procedure TForm12.FreeMyDynaMenu(var Message: TMessage);
begin
dynamicPopupMenu.Free;
end;
最后在Button1Click中,用发布消息替换对Free
的呼叫:
finally
// dynamicPopupMenu.Free;
PostMessage(self.Handle, WM_FREE_MY_DYNAMENU, 0, 0);
在创建菜单时,您也不应将表单指定为所有者:
dynamicPopupMenu := tpopupmenu.create( nil ); // self replaced with nil
还有一种更简单的替代方法(尽管不是我所喜欢的)是使用TTimer
来延迟对Free
的呼叫。