Is there an easy way to call a method in a frame when the form that owns it is activated / deactivated?

This is about Delphi and VCL.

I have several frames that can be used in several forms, usually created and added to the form in the code. It is possible that the form contains several of these frames. I need to execute some code in these frames when the form containing it deactivates and cancels it when activated.

TMyFrame.FormActivated; TMyFrame.FormDeactivated 

One solution is to have a FormActivate / FormDeactivate event handler in those forms that call the frame method.

 procedure TMyForm.OnActivate(_Sender: TObject); begin FFrame1.FormActivated; FFrame2.FormActivated; end; 

Here's how I implemented it at the moment, but it has several drawbacks:

  • I have to implement this in every form containing an instance of these frames
  • The form should know that some frames need this call (hard link)
  • Frames should publish two methods that should be called in these events. I would prefer not to disclose these methods.

Another option is to set the FormActivate / FormDeactivate event inside the frame constructor. But this would mean that no other code could be executed in these events, and it would not work if several such frames in the same form.

Is there another option that works for any form containing several of these frames?

I need this for Delphi 2007, if that matters.

+6
source share
2 answers

(Assuming VCL) The frame must somehow intercept the parent form, activate / deactivate events. There are many possible ways to do this (setting the parent form of the OnActivate / OnDeactivate events, subclasses of SetWindowLong (GWL_WNDPROC) or WindowProc ), but you will need to ensure that if multiple instances of the frame do this in the same instance of the form, the hook removal order must be insert reversed hook. In addition, you will have the problem of properly handling cases when window handlers are restored.

A simpler approach would be to use something like this as the ancestor of all your forms in a 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 the 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; 

Thus, the relationship is fairly loose, and it still allows you to override the default behavior of TMyFrame by overriding the CM_ACTIVATE or CM_DEACTIVATE message handler in descendants that require special handling.

Cautions:

  • This has not been tested - it is just a quick suggestion, as a starting point. You can also declare and use your own custom messages instead of CM_ACTIVATE / CM_DEACTIVATE to avoid any interference with the rest of the VCL.
  • NotifyControls notifies all controls - not just frames - but regular controls ignore / do not process CM_ACTIVATE / CM_DEACTIVATE by default, so this should not be a problem. You can also implement your own NotifyFrames method.
+7
source

I don’t know if this is possible here, but I had a similar problem several years ago and it was solved with some form / frame inheritance. There is a base frame class declaring these methods virtual and a form class that catches events and iterations over all child frames that invoke the corresponding frame method. Received frames override these methods as needed. This reduces grip on base classes.

In later refactoring, this was changed to interfaces implemented using frames, completely eliminating the connection between the form and the frame classes.

+6
source

All Articles