Stop event propagation in Delphi 7
Asked Answered
G

2

6

I'm stuck on a problem in Delphi 7 about event propagation (due to my ignorance).

I am asked to dynamically attach an OnMouseUp event handler on some controls on a form (and I'm fine with that thing), but if the OnMouseUp is present, the OnClick event on that control must not be processed.

Background

If you are asking the reason behind this, well, I'm in charge to modify an old production monitoring application (sigh) that from now on must accommodate a conditional behaviour for some controls, in direct response to a former click on a special function button.

Some of those controls have an OnClick event handler already; the first solution the team came up with was to punctually intervene on each OnClick handler and manage out the contextual actions in relation to the special function button status.

I suggested to take advantage of the Object-Oriented design already in place for the application forms: they all inherit from the same custom ancestor object, so I planned to insert an initialization method there to dynamically attach OnMouseUp events to the controls that are declared to support it in subclasses.

The need

I'm not hereby asking a validation or questioning on the (possible lack of) design goodness about all this (by the way, after a lot of thinking and reasoning it seemed to be the path we can walk with less pain); my problem is that for such design to take place I'd have to let dynamically-attached OnMouseUp event handlers stop event propagation to the pre-existent OnClick events on those controls.

Is it possible with Delphi 7?

Georgenegeorges answered 8/5, 2012 at 10:7 Comment(2)
or, when assigning OnMouseUp, also unassign OnClickRhythmandblues
Not sure if that's what you are trying to achieve, but I've posted an example of how to redirect all the assigned OnClick events to another and optionally restore them to original. Please note, it doesn't answer this question explicitly; it's such kind of a proposal for concept re-design - since the redirection is used, there's no need for an extra OnMouseUp event, you just write another, common OnClick event handler). It uses RTTI and applies to all components having OnClick event handler assigned (but it can easily be limited e.g. to only one component class type).Poulard
P
6

Please note, the following does not explicitly answer the question here. It's more a proposal to the concept re-design (redirect OnClick events instead of adding extra OnMouseUp). It's about how to redirect OnClick event handler (if assigned some) of all components (might be filtered, if needed) to another (common) OnClick event handler. It includes also a method for restoring them to the original state.

In the following example I'll try to show you how to replace and then optionally restore the OnClick event handlers (if the component has written some) by the specific one. This is done to all components having the OnClick event published, so you don't need to know in advance if the component class has OnClick event available or not (but it can very simply be modified to use only a specific class).

The code consists from the following:

  • OnSpecialClick - it is the event handler to what all OnClick events will be binded when you call the ReplaceOnClickEvents procedure, notice that it must be published to be visible for RTTI !!!

  • Button1Click - represents here the old event handler which should be replaced, it is binded to the Button1.OnClick event at design time

  • ReplaceOnClickEvents - method, which iterates through all components on the form and checks
    if the currently iterated one has the OnClick event handler assigned; if so, it stores it into a backup collection and replace this event handler by the OnSpecialClick

  • RestoreOnClickEvents - method, which restores the original OnClick event handlers; it iterates through the backup collection and assign the event methods to its stored component instances

  • CheckBox1Click - this check box click event is meant to be the switch between the common and a special mode (CheckBox1 checked state means to be the special mode), only this OnClick event is not replaced by the ReplaceOnClickEvents call (because you wouldn't be able to restore the mode back to normal)

And here it is:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, TypInfo, StdCtrls, Contnrs;

type
  TEventBackup = class
    Component: TComponent;
    OnClickMethod: TMethod;
  end;

type
  TForm1 = class(TForm)
    Button1: TButton;
    CheckBox1: TCheckBox;
    procedure Button1Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    procedure ReplaceOnClickEvents;
    procedure RestoreOnClickEvents;
  published
    procedure OnSpecialClick(Sender: TObject);
  end;

var
  Form1: TForm1;
  EventBackupList: TObjectList;

implementation

{$R *.dfm}

procedure TForm1.OnSpecialClick(Sender: TObject);
begin
  ShowMessage('Hi, I''m an OnSpecialClick event message!');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Hi, I''m just that boring original OnClick event message!');
end;

procedure TForm1.ReplaceOnClickEvents;
var
  I: Integer;
  Component: TComponent;
  EventMethod: TMethod;
  EventBackup: TEventBackup;
begin
  for I := 0 to ComponentCount - 1 do
  begin
    Component := Components[I];
    if Component = CheckBox1 then
      Continue;

    if IsPublishedProp(Component, 'OnClick') then
    begin
      EventMethod := GetMethodProp(Component, 'OnClick');
      if Assigned(EventMethod.Code) and Assigned(EventMethod.Data) then
      begin
        EventBackup := TEventBackup.Create;
        EventBackup.Component := Component;
        EventBackup.OnClickMethod := EventMethod;
        EventBackupList.Add(EventBackup);
        EventMethod.Code := MethodAddress('OnSpecialClick');
        EventMethod.Data := Pointer(Self);
        SetMethodProp(Component, 'OnClick', EventMethod);
      end;
    end;
  end;
end;

procedure TForm1.RestoreOnClickEvents;
var
  I: Integer;
  EventBackup: TEventBackup;
begin
  for I := 0 to EventBackupList.Count - 1 do
  begin
    EventBackup := TEventBackup(EventBackupList[I]);
    SetMethodProp(EventBackup.Component, 'OnClick', EventBackup.OnClickMethod);
  end;
  EventBackupList.Clear;
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if CheckBox1.Checked then
    ReplaceOnClickEvents
  else
    RestoreOnClickEvents;
end;

initialization
  EventBackupList := TObjectList.Create;
  EventBackupList.OwnsObjects := True;

finalization
  EventBackupList.Free;

end.
Poulard answered 8/5, 2012 at 11:6 Comment(6)
@Simon, it should have, I would suspect the GetMethodProp or SetMethodProp methods are missing, but TypInfo.pas is already in Delphi 7, doesn't it ? Maybe it depends on the type of distribution (if I remember correctly there were some Personal editions and they might be missing it, I don't know). I can't verify that, but see e.g. here (question starts with working on a console application using Delphi 7 and in the code's uses clause is TypInfo).Poulard
Im not knocking the answer it is good :), but no, Delphi 7 doesnt have the full RTTI. Just looking now you are correct about the typinfo.pas class but you cant access properties etc. D2010 was the version were it was vastly improved/implemented.Yate
@Simon, Delphi 7 indeed doesn't have the extended RTTI (the RTTI.pas, which has been introduced in Delphi 2010 as you mentioned), but there is the old style RTTI (the TypInfo.pas unit, which is still available even in the new Delphi versions). Anyway, the reason why the OnSpecialClick event method must be published is just a consequence of the limit of the old style RTTI. My friend confirmed me this works also in Delphi 7, so I would say this you can use from Delphi 7 to Delphi XE2 :-)Poulard
@TLama: Tried your suggestion, it works fine in D7. Thanks a lot ;). We'll change our basic idea to reflect this you suggested.Georgenegeorges
Glad to help ;-) I've used this approach several times, from translation tools to built-in form editors and it's the most comfortable way to redirect events from my point of view.Poulard
@Poulard +1 and thanks, something I wasn't aware of, cheers for the tipYate
M
1

As both TLama and TOndrej have said, there are a few ways to accomplish what you're attempting:

  1. To do if Assigned(Control.OnMouseUp) then Exit; on your OnClick event handler

  2. To "unassign" the OnClick event when assigning OnMouseUp (and vice-versa)

Both approaches will accomplish what you've detailed, though "unassigning" the OnClick event will be best for performance (to an infintismally small extent) since you won't be performing the if statement repeatedly.

Multifoliate answered 8/5, 2012 at 10:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.