Search code examples
delphidelphi-5

Extend the event OnClick of all MenuItems in the screen to execute another block of code


I want to set an event OnClick to all TMenuItems on the screen to do what the event currently does, and another few lines of code. I am currently using Delphi 5

For example, say that I have a TMenuItem with the code:

procedure TdesktopForm.MenuFoo1Click(Sender: TObject);
begin
  ShowMessage(TComponent(Sender).Name)
end;

and I also have the following procedure:

procedure TdesktopForm.bar;
begin
  ShowMessage('extra')
end;

And I want to everytime I click the TMenuItem the program show the TMenuItem's name and also the 'extra' message.

The example shown is just a demonstration of my problem, as in the real software I have over 300 menu items, I want to do this generically, so I won't have to add extra lines of code to all current menu clicks, nor add them when I add new menu items. The order of execution (between the menu click and the extra block of code) doesn't matter.

I tried using TActionList but I couldn't retrieve the object triggering the action, hence, I can't print it's name. I tried using ActiveControl but it always return the focused currently focused object, not the actual menu that I clicked. And also, the TAction execute event overwrites my TMainMenu.OnClick event


Solution

  • As long as all your event handlers are assigned at some point (either at design time or at run time) and don't change afterwards, you can do something like this:

    1. Enumerate all menu items in the menu
    2. For each create an object like the one described below

      type
        TEventInterceptor = class(TComponent)
        private
          FOrigEvent: TNotifyEvent;
          FAdditionalEvent: TNotifyEvent;
          procedure HandleOnClick(_Sender: TObject);
        public
          constructor Create(_MenuItem: TMenuItem; _AdditionalEvent: TNotifyEvent);
        end;
      
      constructor TEventInterceptor.Create(_MenuItem: TMenuItem; _AdditionalEvent: TNotifyEvent);
      begin
        inherited Create(_MenuItem);
        FOrigEvent := _MenuItem.OnClick;
        FAdditionalEvent := _AdditionalEvent;
        _MenuItem.OnClick := HandleOnClick;
      end;
      
      procedure TEventInterceptor.HandleOnClick(_Sender: TObject);
      begin
        FOrigEvent(_Sender);
        FAdditinalEvent(_Sender);
      end;
      

    Note that this code is completely untested and may not even compile. I'm also not sure whether this works with Delphi 5. It does with Delphi 6 though, so chances are good.

    Edit: Some additional notes (thanks for the comments):

    • Inheriting this class from TComponent makes the form free it automatically when it is being destroyed.
    • HandleOnClick should possibly check if FOrigEvent is assigned before calling it.