16

当触发 TAction 事件时,“发送者”始终是操作本身。通常这是最有用的,但是否有可能找出谁触发了操作的 OnExecute 事件?

例子

假设您有一个包含以下内容的表单:

  • 2 个按钮,称为Button1Button2
  • 1 个 TAction 被调用actDoStuff

相同的动作被分配给两个按钮。是否可以显示我单击了哪个按钮?

例子.dfm

object Form1: TForm1
  object Button1: TButton
    Action = actDoStuff
  end
  object Button2: TButton
    Action = actDoStuff
    Left = 100
  end
  object actDoStuff: TAction
    Caption = 'Do Stuff'
    OnExecute = actDoStuffExecute
  end
end

例子.pas

unit Example;
interface
uses Windows, Classes, Forms, Dialogs, Controls, ActnList, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    actDoStuff: TAction;
    procedure actDoStuffExecute(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation    
{$R *.dfm}

procedure TForm1.actDoStuffExecute(Sender: TObject);
begin
  ShowMessage('Button X was clicked');
end;

end.

我目前看到的唯一解决方案是不使用按钮的 action 属性,而是为每个按钮设置一个事件处理程序,然后从那里调用 actDoStuffExecute() ,但这违背了首先使用操作的全部目的。

我也不希望对每个单独的控件都有专门的操作。上面的示例是我面临的问题的简化版本。我有一个带有可变数量的菜单项(文件名)的菜单,每个菜单项基本上都必须做同样的事情,除了加载另一个文件。为每个菜单项设置操作有点愚蠢。

4

7 回答 7

30

尝试使用ActionComponent属性:

存储导致此操作执行的客户端组件。

使用ActionComponent来辨别是哪个客户端组件导致该操作执行。例如,如果您需要了解触发此操作的用户操作,请从OnExecute事件处理程序中检查ActionComponent 。

当用户单击客户端控件时,该客户端会在调用操作的Execute方法之前设置ActionComponent 。动作执行后,动作将ActionComponent重置为 nil。

例如:

  ShowMessage( (Sender as TAction).ActionComponent.Name );

当我分别单击第一个和第二个按钮时,使用这个我得到“Button1”和“Button2”。

于 2010-07-21T20:43:03.523 回答
10

知道是什么按钮触发了动作,这与使用动作的观点背道而驰——动作可能由按钮单击、菜单单击或任何数量的其他用户活动触发。存在动作来统一按钮和菜单之间的启用/禁用和单击处理的状态管理。

如果您想知道哪个按钮触发了操作,因为您想执行稍微不同的操作,或者以不同的方式“调整”操作,那么 TAction 可能不是您想要做的正确解决方案。

于 2010-07-21T19:19:08.687 回答
3

而不是动作,只需使用点击事件。将所有按钮设置为使用相同的事件处理程序。理想情况下,不要以第一个按钮命名(您可以重命名它)。

这是代码:

Procedure TMyForm.DestinationButtonClickHandlerThing(Sender: TObject); 
begin
  if Sender = Btn_ViewIt then
  begin
    // View It
  end
  else if Sender = Btn_FaxIt then
  begin
    // Fax It
  end
  else if Sender = Btn_ScrapIt then
  begin
    // Scrap It
  end
  else 
    ....   // error
   ...
end;
于 2010-07-21T20:10:20.450 回答
1

在某些情况下,相同的操作应适用于类似的控制。问题与

ShowMessage( (Sender as TAction).ActionComponent.Name );

是当一个说弹出菜单调用该操作时,您将获得弹出菜单的名称。你可以使用:

procedure TMyForm.actMyActionExecute(Sender: TObject);
var
  LMyControl: TMyControl;
begin
  if Screen.ActiveControl.Name = 'MyControl1' then
    LMyControl = Sender as TMyControl
  else
    Exit;
  // Use the local variable for whatever needed
end;
于 2014-01-31T00:18:57.407 回答
1

我有一堆面板,我想让用户右键单击这些面板中的任何一个并执行“删除文件”操作。所以,我有一个与所有这些面板相关联的弹出菜单。这就是我找出右键单击哪个面板的方法:

(注意:我放了很多注释来清楚地解释它是如何工作的。但是如果你不喜欢它,你可以将代码压缩到 2 行(参见第二个过程))。

因此,如果您为该弹出菜单分配了操作:

procedure Tfrm.actDelExecute(Sender: TObject);
VAR
  PopMenu: TPopupMenu;
  MenuItem: TMenuItem;
  PopupComponent: TComponent;
begin
 { Find the menuitem associated to this action }
 MenuItem:= TAction(Sender).ActionComponent as TMenuItem;  { This will crash and burn if we call this from a pop-up menu, not from an action! But we always use actions, so.... }

 { Was this action called by keyboard shortcut? Note: in theory there should be no keyboard shortcuts for this action if the action can be applyed to multiple panels. We can call this action ONLY by selecting (right click) a panel! }
 if MenuItem = NIL then
  begin
   MsgError('This action should not be called by keyboard shortcuts!');
   EXIT;
  end;

 { Find to which pop-up menu this menuitem belongs to }
 PopMenu:= (MenuItem.GetParentMenu as TPopupMenu);

 { Find on which component the user right clicks }
 PopupComponent := PopMenu.PopupComponent;

 { Finally, access that component }
 (PopupComponent as TMonFrame).Delete(FALSE);
end;

如果您只有一个简单的弹出菜单(未分配任何操作):

procedure Tfrm.actDelHddExecute(Sender: TObject);
VAR PopupComponent: TComponent;
begin
 PopupComponent := ((Sender as TMenuItem).GetParentMenu as TPopupMenu).PopupComponent;
 (PopupComponent as TMonFrame).Delete(TRUE);
end;

您可以将所有代码放在一个返回 TPanel 的函数中,并像这样调用它:

procedure Tfrm.actDelWallExecute(Sender: TObject);
begin
 if GetPanelFromPopUp(Sender) <> NIL
 then GetPanelFromPopUp(Sender).Delete(FALSE);
end;
于 2020-07-18T13:40:22.020 回答
0

好的,与此同时,我想我找到了一个可行的解决方案..

我可以让所有控件使用相同的操作;我只需要覆盖他们的 OnClick 事件处理程序,我只需要一个处理程序来处理所有这些事件。

我仍然很想知道是否有可能找出哪个控件触发了该操作,但是对于我当前的应用程序,我正在使用类似于以下代码的解决方案:

unit Example;

interface

uses
  Windows, Classes, Forms, Dialogs, Controls, ActnList, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    actDoStuff: TAction;
    procedure actDoStuffExecute(Sender: TObject);
    procedure ButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.actDoStuffExecute(Sender: TObject);
begin
  ShowMessage('Button '+TControl(Sender).Name +' was clicked')
end;

procedure TForm1.ButtonClick(Sender: TObject);
begin
  actDoStuffExecute(Sender)
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.OnClick := ButtonClick;
  Button2.OnClick := ButtonClick
end;

end.
于 2010-07-21T19:11:53.530 回答
0

将按钮的标签设置为 1、2、... 等,然后:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.OnClick := ButtonClick;
  Button2.OnClick := ButtonClick;
end;

procedure TForm1.ButtonClick(Sender: TObject);
begin
  if Sender is TButton then
  begin
    Caption := 'Button: ' + IntToStr(TButton(Sender).Tag);
  end;  
end;
于 2010-07-21T19:41:01.003 回答