How to make my custom control be notified when his form or application receives and loses focus?

294 views Asked by At

I want my control to receive distinct notifications only when it's parent form (not panel or something else, just the main form of this control) receives and loses focus. Doesn't matter if the focus is switched from another form of the application or between my application and other application, it must be received for both cases. Is it possible? I want to suspend some updates of the control when his form is not active and resume the updates when the form is activated.

Edit: In other words, the control must catch the (TForm.OnActivate + TApplication.OnActivate) and (TForm.OnDeactivate + TApplication.OnDeactivate)

Edit2: If it's not possible both, at least if I can make the control catch the events from TApplication. It's more important than those from TForm.

1

There are 1 answers

1
NGLN On BEST ANSWER

I want to suspend some updates of the control when his form is not active and resume the updates when the form is activated.

If those updates are done continuously, or are being triggered by a timer or actions, then you could be done with:

type
  TMyControl = class(TControl)
  private
    procedure PerformUpdate;
  end;

procedure TMyControl.PerformUpdate;
begin
  if Application.Active and HasParent and GetParentForm(Self).Active then
    //...
  else
    //...
end;

...at least if I can make the control catch the events from the application

Catching TApplication.OnActivate and TApplication.OnDeactivate is pretty easy with a TApplicationEvents component:

uses
  Vcl.AppEvnts;

type
  TMyControl = class(TControl)
  private
    FActive: Boolean;
    FAppEvents: TApplicationEvents;
    procedure ApplicationActiveChanged(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override;
  end;

procedure TMyControl.ApplicationActiveChanged(Sender: TObject);
begin
  FActive := Application.Active;
end;

constructor TMyControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FAppEvents := TApplicationEvents.Create(Self);
  FAppEvents.OnActivate := ApplicationActiveChanged;
  FAppEvents.OnDeactivate := ApplicationActiveChanged;
end;

...it's more important than those from the form

Catching the (de)activation of the parenting form can be done in Application.OnIdle. All this combined could result in something like this:

type
  TMyControl = class(TControl)
  private
    FActive: Boolean;
    FAppEvents: TApplicationEvents;
    FParentForm: TCustomForm;
    procedure ApplicationActiveChanged(Sender: TObject);
    procedure ApplicationIdle(Sender: TObject; var Done: Boolean);
    procedure UpdateActive;
  protected
    procedure SetParent(AParent: TWinControl); override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

procedure TMyControl.ApplicationActiveChanged(Sender: TObject);
begin
  UpdateActive;
end;

procedure TMyControl.ApplicationIdle(Sender: TObject; var Done: Boolean);
begin
  UpdateActive;
  Done := True;
end;

constructor TMyControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FAppEvents := TApplicationEvents.Create(Self);
  FAppEvents.OnActivate := ApplicationActiveChanged;
  FAppEvents.OnDeactivate := ApplicationActiveChanged;
end;

procedure TMyControl.SetParent(AParent: TWinControl);
begin
  inherited SetParent(AParent);
  FParentForm := GetParentForm(Self);
end;

procedure TMyControl.UpdateActive;
var
  SaveActive: Boolean;
begin
  SaveActive := FActive;
  FActive := Application.Active and (FParentForm <> nil) and FParentForm.Active;
  if Application.Active then
    FAppEvents.OnIdle := ApplicationIdle
  else
    FAppEvents.OnIdle := nil;
  if FActive <> SaveActive then
    Invalidate;
end;

Because using Application.OnIdle is quite a rigorous method, spare its use like I did above by only assigning it when necessary and speed up its implementation by caching function results like GetParentForm.