简体   繁体   English

如何查看谁触发了 Delphi 中的操作?

[英]How can I see who triggered an action in Delphi?

When a TAction event fires, the "Sender" is always the action itself.当触发 TAction 事件时,“发送者”始终是操作本身。 Usually that's the most useful, but is it somehow possible to find out who triggered the action's OnExecute event?通常这是最有用的,但是否有可能找出谁触发了操作的 OnExecute 事件?

Example例子

Let's say you have a form with the following:假设您有一个包含以下内容的表单:

  • 2 buttons, called Button1 and Button2 2 个按钮,分别称为Button1Button2
  • 1 TAction called actDoStuff 1 TAction 称为actDoStuff

The same action is assigned to both buttons.相同的动作被分配给两个按钮。 Is it possible to show which button I clicked?是否可以显示我单击了哪个按钮?

Example.dfm例子.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

Example.pas例子.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.

The only solution I see at the moment is to not use the action property of buttons, but having an eventhandler for each button, and calling actDoStuffExecute() from there, but that sort of defies the whole purpose of using actions in the first place.我目前看到的唯一解决方案是不使用按钮的 action 属性,而是为每个按钮设置一个事件处理程序,并从那里调用 actDoStuffExecute() ,但这违背了首先使用操作的全部目的。

I don't want to have a dedicated action for each separate control either.我也不希望对每个单独的控件都有专门的操作。 The example above is a simplified version of the problem that I'm facing.上面的示例是我面临的问题的简化版本。 I have a menu with a variable number of menu items (file names), and each menu item basically has to do the same thing, except for loading another file.我有一个带有可变数量的菜单项(文件名)的菜单,每个菜单项基本上都必须做同样的事情,除了加载另一个文件。 Having actions for each menu item would be a bit silly.为每个菜单项设置操作有点愚蠢。

Try using the ActionComponent property:尝试使用ActionComponent属性:

Stores the client component that caused this action to execute.存储导致此操作执行的客户端组件。

Use ActionComponent to discern which client component caused this action to execute.使用ActionComponent来辨别哪个客户端组件导致执行此操作。 For example, examine ActionComponent from an OnExecute event handler if you need to know what user action triggered this action.例如,如果您需要了解触发此操作的用户操作,请检查OnExecute事件处理程序中的ActionComponent

When the user clicks a client control, that client sets ActionComponent before calling the action's Execute method.当用户单击客户端控件时,该客户端会在调用操作的Execute方法之前设置ActionComponent After the action executes, the action resets ActionComponent to nil.动作执行后,动作将ActionComponent重置为 nil。

For example:例如:

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

Using this I get "Button1" and "Button2" when I click the first and second button respectively.使用它,当我分别单击第一个和第二个按钮时,我会得到“Button1”和“Button2”。

Knowing what button triggered the action sort of goes against the point of using actions - an action may be triggered by a button click, or a menu click, or any number of other user activities.知道哪个按钮触发了动作与使用动作的点背道而驰——动作可能由按钮点击、菜单点击或任意数量的其他用户活动触发。 Actions exist to unify the state management of enable/disabled and click handling between buttons and menus.存在操作来统一启用/禁用状态管理以及按钮和菜单之间的单击处理。

If you want to know which button fired the action because you want to perform a slightly different operation, or "flavor" the operation differently, then perhaps TAction isn't the right solution for what you want to do.如果您想知道哪个按钮触发了操作,因为您想执行稍微不同的操作,或者以不同的方式“调味”操作,那么 TAction 可能不是您想要执行的操作的正确解决方案。

Instead of actions, just use a click event.而不是动作,只需使用点击事件。 Set all buttons to use the same event handler.将所有按钮设置为使用相同的事件处理程序。 Ideally, NOT named after the first button (you can rename it).理想情况下,不要以第一个按钮命名(您可以重命名)。

Here's the code:这是代码:

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;

There are situations where the same action should apply to similar controls.在某些情况下,相同的操作应该适用于类似的控制。 The problem with问题在于

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

is that when the action is invoked by a say popup menu, you get the popup menu's name.是当动作被一个 say 弹出菜单调用时,你会得到弹出菜单的名称。 You could use:你可以使用:

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;

I have a bunch of panels and I want to let the user right click any of those panels and perform a "delete file" action.我有一堆面板,我想让用户右键单击这些面板中的任何一个并执行“删除文件”操作。 So, I have a single pop-up menu associated with all those panels.所以,我有一个与所有这些面板相关联的弹出菜单。 This is how I find out which panel was right clicked:这就是我找出哪个面板被右键单击的方法:

(Note: I put lots of comments to clearly explain how it works. But if you don't like it, you can compactify the code to 2 lines (see the second procedure)). (注意:我放了很多注释来清楚地解释它是如何工作的。但如果你不喜欢它,你可以将代码压缩为 2 行(参见第二个过程))。

So, if you have actions assigned to that pop-up menu:因此,如果您为该弹出菜单分配了操作:

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;

If you only have a simple pop-up menu (no actions assigned):如果您只有一个简单的弹出菜单(未分配操作):

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

You can put all that code in a single function that returns a TPanel and call it like this:您可以将所有代码放在一个返回 TPanel 的函数中,并像这样调用它:

procedure Tfrm.actDelWallExecute(Sender: TObject);
begin
 if GetPanelFromPopUp(Sender) <> NIL
 then GetPanelFromPopUp(Sender).Delete(FALSE);
end;

Ok, in the meanwhile I think I found a workable solution..好的,同时我想我找到了一个可行的解决方案..

I can have all controls use the same action;我可以让所有控件使用相同的操作; I just need to override their OnClick event handler, and I just need a single handler for all of them.我只需要覆盖他们的 OnClick 事件处理程序,我只需要一个处理程序即可。

I'm still interested to know if it's possible to find out which control triggered the action, but for my current application I'm using a solution that's similar to the code below:我仍然很想知道是否有可能找出哪个控件触发了该操作,但对于我当前的应用程序,我正在使用类似于以下代码的解决方案:

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.

set the Tag of the buttons as 1, 2, ... etc and then:将按钮的标签设置为 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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM