21
votes

Having a popup menu attached to several components on a form (buttons, but also things like TCharts), I would like to know which component was right clicked to start the popup menu in the first place.

The Sender parameter of the click method just points to the TMenuItem, its parent to the popup menu (or the parenting menu item).

How do I get the originating component?

5

5 Answers

39
votes

Did you mean PopupMenu1.PopupComponent ?

14
votes

You can get the caller component within the click event of the TMenuItem of a PopupMenu by

Caller := ((Sender as TMenuItem).GetParentMenu as TPopupMenu).PopupComponent;

An Example of a PopupMenu which is assigned to several list boxes and solves the export to file functionality:

procedure TForm1.mniExportFileClick(Sender: TObject);
var Caller: TObject;
begin  
  if SaveTextFileDialog1.Execute then
  begin
    Caller := ((Sender as TMenuItem).GetParentMenu as TPopupMenu).PopupComponent;
    (Caller as TListBox).Items.
      SaveToFile(SaveTextFileDialog1.FileName,
        StandardEncodingFromName(
          SaveTextFileDialog1.Encodings[SaveTextFileDialog1.EncodingIndex]));
  end;
end; 
0
votes

As a last resort you can use Mouse.CursorPos in TPopupMenu.OnPopup to find this component on a form. But there is probably a better/easier way.

0
votes

PopUpMenu.PopupComponent indicates the component that last displayed the popup menu in response to the right mouse click

0
votes

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)).

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:

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