Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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:

  • I have to implement this in every form containing an instance of these frames
  • The form has to know that some frames need this call (tight coupling)
  • The frames needs to publish two methods to be called in these events. I'd rather not expose these methods.

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.

like image 302
dummzeuch Avatar asked Jun 03 '16 12:06

dummzeuch


2 Answers

(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:

  1. This has not been tested - it's just a quick suggestion, as a starting point. You might also declare and use your own custom messages instead of CM_ACTIVATE/CM_DEACTIVATE, to avoid any interference with the rest of the VCL.
  2. 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.
like image 176
Ondrej Kelle Avatar answered Nov 15 '22 08:11

Ondrej Kelle


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.

like image 41
Uwe Raabe Avatar answered Nov 15 '22 07:11

Uwe Raabe