动态创建的TPopup菜单不会在Delphi XE4中调用其项目的OnClick事件处理程序

时间:2018-09-15 14:13:49

标签: delphi

我想测试一些东西,并创建了一个简单的动态弹出菜单示例。我必须意识到弹出菜单可以正常显示,但它不会调用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

2 个答案:

答案 0 :(得分:2)

由于您要使用当前的dynamicPopupMenu实例创建TForm1Owner一样,我不确定自己是否需要垃圾收集器的所有功能, 因为TForm1实例本身销毁后仍会销毁。

尝试一下:

  1. 在Classs.Pas中的TComponent.Destroy上设置断点,并在Tag上设置手表。

  2. 如下所示更改您的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的呼叫。