如何使我的自定义控件在其所在的窗体或应用程序获得焦点和失去焦点时被通知?

3
我希望我的控件只在其父窗体(不是面板或其他内容,仅此控件的主表单)获得和失去焦点时接收到不同的通知。无论焦点是从应用程序的另一个窗体还是在我的应用程序和其他应用程序之间切换,都必须接收到。是否可能?当控件所在的表单未处于活动状态时,我想暂停一些更新并在表单被激活后恢复更新。
编辑:换句话说,该控件必须捕获(TForm.OnActivate + TApplication.OnActivate)和(TForm.OnDeactivate + TApplication.OnDeactivate)。
编辑2:如果两者都不可能,至少如果我可以使该控件捕获来自TApplication的事件。这比从TForm获得的事件更重要。
1个回答

3
我希望在控件所在的窗体不处于活动状态时,暂停一些更新,并在窗体激活时恢复更新。
如果这些更新是连续进行的,或者由计时器或操作触发的话,您可以使用以下方法:
type
  TMyControl = class(TControl)
  private
    procedure PerformUpdate;
  end;

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

至少,如果我可以让控件获取应用程序的事件,那就好了。
使用 TApplicationEvents 组件轻松捕获 TApplication.OnActivateTApplication.OnDeactivate 事件:
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;

...这比来自表单的更重要

可以在Application.OnIdle中捕获父窗体的(去)激活状态。所有这些组合起来可能会得到如下结果:

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;

因为使用Application.OnIdle方法是相当严谨的,所以像我上面那样只在必要时进行分配,通过缓存函数结果如GetParentForm来加快其实现速度。


非常感谢您提供这段代码。 我偶尔编写自定义组件,以前不知道如何涉及AppEvnts。 这会为我节省很多时间。 再次感谢。 - Learning

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接