Как я могу увидеть, кто вызвал действие в Delphi?
Когда действие даже запускается, "отправитель" всегда является самим действием. Обычно это наиболее полезно, но возможно ли как-то выяснить, кто вызвал событие onexecute действия?
пример
Допустим, у вас есть форма со следующим:
- 2 кнопки, называемые
Button1
а такжеButton2
- 1 TAction вызывается
actDoStuff
Одно и то же действие назначено обеим кнопкам. Можно ли показать, на какую кнопку я нажал?
Example.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
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(), но этот вид в первую очередь бросает вызов всей цели использования действий.
Я также не хочу иметь отдельное действие для каждого отдельного элемента управления. Приведенный выше пример является упрощенной версией проблемы, с которой я сталкиваюсь. У меня есть меню с переменным количеством пунктов меню (имен файлов), и каждый пункт меню в основном должен делать то же самое, за исключением загрузки другого файла. Иметь действия для каждого пункта меню было бы немного глупо.
6 ответов
Попробуйте использовать свойство ActionComponent:
Хранит клиентский компонент, который вызвал выполнение этого действия.
Используйте ActionComponent, чтобы определить, какой клиентский компонент вызвал выполнение этого действия. Например, проверьте ActionComponent из обработчика события OnExecute, если вам нужно знать, какое действие пользователя вызвало это действие.
Когда пользователь щелкает элемент управления клиента, этот клиент устанавливает ActionComponent перед вызовом метода Execute действия. После того, как действие выполнено, действие сбрасывает ActionComponent в ноль.
Например:
ShowMessage( (Sender as TAction).ActionComponent.Name );
Используя это, я получаю "Button1" и "Button2", когда я нажимаю первую и вторую кнопку соответственно.
Знание того, какая кнопка активировала действие, идет вразрез с точкой использования действий - действие может быть вызвано нажатием кнопки, нажатием меню или любым другим действием пользователя. Существуют действия для унификации управления состоянием включения / выключения и обработки щелчков между кнопками и меню.
Если вы хотите знать, какая кнопка активировала действие, потому что вы хотите выполнить немного другую операцию или "по-разному" адаптировать операцию, то, возможно, TAction не является правильным решением для того, что вы хотите сделать.
Вместо действий просто используйте событие щелчка. Установите все кнопки для использования одного и того же обработчика событий. В идеале, НЕ назван в честь первой кнопки (вы можете переименовать ее).
Вот код:
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;
У меня есть несколько панелей, и я хочу позволить пользователю щелкнуть правой кнопкой мыши любую из этих панелей и выполнить действие "удалить файл". Итак, у меня есть одно всплывающее меню, связанное со всеми этими панелями. Вот как я узнаю, какая панель была нажата правой кнопкой мыши:
(Примечание: я поместил много комментариев, чтобы четко объяснить, как это работает. Но если вам это не нравится, вы можете сжать код до 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;
Хорошо, в то же время я думаю, что я нашел работоспособное решение..
Я могу иметь все элементы управления использовать одно и то же действие; Мне просто нужно переопределить их обработчик событий 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.
Существуют ситуации, когда одно и то же действие должно применяться к аналогичным элементам управления. Проблема с
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;
Установите тег кнопок как 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;