Well, I now have working a generic way of overriding events that will
ensures my components won't interfere with each other while doing this - it
was a bit of an effort though. In the end it boils down to three
procedures:
type PMethod = ^TMethod;
procedure HookProc(Wrapper: TObject; Wrapped: TComponent; PWrappedEvent:
PMethod; NewEvent: TMethod);
procedure UnhookProc(Wrapper: TObject; Wrapped: TComponent; PWrappedEvent:
PMethod);
function PriorHook(Wrapper: TObject; Wrapped: TComponent; PWrappedEvent:
PMethod): TMethod;
I use HookProc and UnhookProc just as you'd think, and PriorHook to call the
last event in the chain before the current one. So what does this all give
me? Well, if I have two components hooking the same event there will be no
conflict; when either unhooks or is free'd (there are FreeNotifications for
the Wrapped component as well as the Wrapper if it's a component - if not
you just have to be sure to call UnhookProc explicitly) then its event will
be removed from wherever it is in the chain (a global class manages all
this). I still have a problem if some other component overrides an event
the old way (ie. not using these procedures), but I've pretty much resigned
myself to accepting there is no way around this other than hoping it won't
ever happen.
The use of the procedures is a little ugly but I don't know of any way to
improve them. Still, they appear to work - I now have to give them some
more complicated tests.
As an example, one of my components hooks into its parent's WindowProc when
its parent is assigned in order to trap some messages. This was the old
SetParent code:
procedure TdseQGrid.SetParent(AParent: TWinControl);
var LParent: TWinControl;
begin
LParent := Parent;
if (AParent <> LParent) then begin
if LParent <> nil then LParent.WindowProc := FParentWindowProc;
inherited;
if AParent <> nil then begin
FParentWindowProc := AParent.WindowProc;
AParent.WindowProc := ParentWindowProc;
end;
end
else inherited;
end;
Note that this required a SetParent(nil) before the component is free'd
(which happens anyway). This is the new:
procedure TdseQGrid.SetParent(AParent: TWinControl);
var LParent: TWinControl;
LMethod: TMethod;
begin
LParent := Parent;
if (AParent <> LParent) then begin
if LParent <> nil then UnhookProc(Self, Parent,
@TMethod(LParent.WindowProc));
inherited;
if AParent <> nil then begin
LMethod.Code := MethodAddress('ParentWindowProc'); // Unfortunately
this is the only way we can access ParentWindowProc as a TMethod
LMethod.Data := Self;
HookProc(Self, AParent, @TMethod(AParent.WindowProc), LMethod);
end;
end
else inherited;
end;
Actually I don't know whether the comment above is true - maybe someone
knows a better way? The point is that the compiler won't let me go
HookProc(Self, AParent, @TMethod(AParent.WindowProc),
TMethod(ParentWindowProc)); // Invalid typecast
This has a further disadvantage in that MethodAddress will only work if
ParentWindowProc is published. I tried using GetMethodProp(Self,
'ParentWindowProc') instead of MethodAddress but it resulted in access
violations - don't know why.
The @'s have to be there to allow the procedures to set the value of the
method as appropriate internally - var parameters are out of the question.
Lastly in ParentWindowProc, instead of the old
if ... then begin
...
end
else FParentWindowProc(Message);
I now have to use
else TWndMethod(PriorHook(Self, Parent,
@TMethod(Parent.WindowProc)))(Message);
TWndMethod or TNotifyEvent or whatever it happens to be, followed by the
parameter list. Ugly I know. Still, in retrospect, perhaps surprising that
it could be done at all...
Any improvements gratefully accepted!
Cheers,
Carl
---------------------------------------------------------------------------
New Zealand Delphi Users group - Delphi List - [EMAIL PROTECTED]
Website: http://www.delphi.org.nz