This is about Delphi and the VCL.
I have got several frames that can be used in multiple forms, usually created and added to the form in code. It's possible that a form contains several of these frames. I need to execute some code in these frames when the form that contains it gets deactivated and reverse this when it gets activated.
TMyFrame.FormActivated;
TMyFrame.FormDeactivated
One solution is to have a FormActivate / FormDeactivate event handler in those forms that call a method of the frames.
procedure TMyForm.OnActivate(_Sender: TObject);
begin
FFrame1.FormActivated;
FFrame2.FormActivated;
end;
This is how I implemented it for now, but that has several disadvantages:
Another option would be to set a FormActivate / FormDeactivate event from within the constructor of the frame. But this would mean that no other code could be executed in these events and it won't work if there are several of these frames on the same form.
Is there any other option that works for any form containing several of these frames?
I need this for Delphi 2007 if this is important.
(Assuming VCL) The frame should somehow intercept the parent form's activate/deactivate events. There are many possible ways to do this (setting parent form's OnActivate
/OnDeactivate
events, subclassing with SetWindowLong
(GWL_WNDPROC)
or WindowProc
) but you'd have to enforce that if multiple frame instances do this to the same form instance the order of removing the hook must be the reverse of inserting the hook. Also, you'd have the problem of correctly handling the cases when window handles get recreated.
An easier approach could be to use something like this as an ancestor of all your forms in the project:
TMyForm = class(TForm)
procedure Activate; override;
procedure Deactivate; override;
end;
procedure TMyForm.Activate;
begin
inherited Activate;
NotifyControls(CM_ACTIVATE);
end;
procedure TMyForm.Deactivate;
begin
inherited Deactivate;
NotifyControls(CM_DEACTIVATE);
end;
and something like this as an ancestor of all your frames in the project:
TMyFrame = class(TFrame)
procedure CMActivate(var Msg: TCMActivate); message CM_ACTIVATE;
procedure CMDeactivate(var Msg: TCMDeactivate); message CM_DEACTIVATE;
end;
procedure TMyFrame.CMActivate(var Msg: TCMActivate);
begin
// parent form activated
end;
procedure TMyFrame.CMDeactivate(var Msg: TCMDeactivate);
begin
// parent form deactivated
end;
This way the coupling is quite loose and it still allows you to override the default TMyFrame
behaviour by overriding the CM_ACTIVATE
or CM_DEACTIVATE
message handler in descendants which need special handling.
Caveats:
CM_ACTIVATE
/CM_DEACTIVATE
, to avoid any interference with the rest of the VCL.NotifyControls
notifies all controls - not only frames - but normal controls ignore/don't handle CM_ACTIVATE
/CM_DEACTIVATE
messages by default so it shouldn't be a problem. You could also implement your own NotifyFrames
method.Don't know if this is feasible here, but I had a similar problem some years ago and solved it with some sort of form/frame inheritance. There is a base frame class declaring these methods virtual and a form class that catches the events and iterates over all child frames calling the appropriate frame method. The derived frames override those methods as needed. This reduces coupling to the base classes.
In a later refactoring this was changed to interfaces implemented by the frames, eliminating the coupling between the form and frame classes completely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With