如何防止在从主窗体显示和/或关闭次要窗体时失去焦点?

3
在主窗体中显示两个次要窗体,然后关闭这两个窗体会导致主窗体失去焦点(另一个应用程序被激活而不是我的)。这些次要窗体可以直接由主窗体创建,也可以通过从第二个窗体创建第三个窗体来创建。在OnClose事件中,次要窗体设置为caFree。使用Delphi 2009(更新3和4)和XP SP3。重现问题的步骤如下:创建新的VCL表单应用程序,将OnClose事件分配如上所述,在创建的表单上拖动按钮,在单击处理程序中创建一个新的TForm1并像下面一样显示它。运行程序。单击按钮以显示第二个窗体。单击第二个窗体上的按钮以创建第三个窗体。关闭这两个新窗体时,主窗体将失去焦点。以下是按钮单击事件处理程序中的代码:with TForm1.Create(Application) do show;有没有办法阻止我的主窗体失去焦点?

有趣的是,当直接从主表单创建两个次要表单时,问题只会在关闭第一个创建的表单后出现,然后关闭第二个创建的表单。


过去我曾经遇到同样的问题,通过更新delphi安装程序解决了这个问题,但在那种情况下,我没有在OnClose事件中使用caFree,这就是这个bug的原因。

一个建议是将次要窗体的Parent属性设置为主窗体,这会使新窗体绑定到主窗体,而我不想要这样。 (并且在那里提出的解决方案始终重新激活主窗体会导致窗体的激活顺序丢失)

4个回答

3

在一个表单关闭之前,我会通过API调用手动激活“拥有”窗口:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  SetForegroundWindow(GetWindowLong(Handle, GWL_HWNDPARENT));
end;

这对于操作系统来说不是问题(即没有闪烁的任务栏按钮),因为我们的应用程序已经在前台运行。
如果设置了MainFormOnTaskBar,则拥有窗口将是我们的主窗体;如果没有,则为隐藏的应用程序窗口。无论哪种情况,应用程序都将保持在前台。
当关闭最后一个窗体-主窗体时,SetForegroundWindow调用是多余的,甚至会失败,如果MainFormOnTaskBar为true,因为此时主窗体将不被拥有,但我不太关心它(再次调用之前可以进行测试)。

1
为了防止主窗体失去焦点,您需要将其注释掉。
// Application.MainFormOnTaskBar := True;

正如@Serg已经建议的那样。这种方法的缺点,正如您已经注意到的那样,是次要窗体可以出现在主窗体后面。通过将窗体的PopupMode设置为pmAuto,可以轻松地防止这种情况发生,这将确保由一个窗体创建的窗体始终位于创建它们的窗体之上。

然而,这也确保了从次要窗体创建的窗体在创建它们的窗体关闭时被关闭。例如:

  • 主窗体创建Secondary1
  • Secondary1创建Secondary2和Secondary3

关闭Secondary1也会关闭Secondary2和Secondary3。

如果这是不希望的行为,您可以通过显式设置PopupParent来更好地控制。例如,将所有窗体都设置为应用程序的主窗体的“parent”:

procedure TForm1.FormCreate(Sender: TObject);
begin
  PopupMode := pmAuto;
  if Self <> Application.MainForm then
    PopupParent := Application.MainForm;
end;

这将确保 Application.MainForm 始终位于所有其他窗体后面;所有其他窗体可以切换到前景;并且当主窗体关闭时,所有窗体都将关闭。


将PopupMode设置为pmAuto会带回原来失去焦点的问题。 - Yona
是的,这就是为什么你还需要将 MainFormOnTaskBar 注释掉或设置为 false。只有这两者都做了才有效。 - Marjan Venema
1
他的意思是,如果你设置 PopupMode := pmAuto,即使 MainFormOnTaskBar := false,焦点问题仍然存在。 - Andreas Rejbrand
@Andreas:嗯,这方法解决了我的问题,但是当我回到D2009检查时,它现在又出现了问题。该死... - Marjan Venema
@Andreas,@Y Low:唉,如果你激活主窗体以查看它是否留在次要窗体后面,然后只单击这些次要窗体的关闭叉号,它们就不会获得焦点,主窗体仍然处于活动状态。被这个问题愚弄了。回到调查中... - Marjan Venema

0
一个快速而简单的解决方法是在项目源代码中注释掉 MainFormOnTaskbar 行:
program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
//  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

已更新

如果您希望主表单始终位于其他窗体后面,还应该重写CreateParams。以下代码可以按照您的期望工作,尽管我怀疑它可能会因为其他原因而不可用:

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TForm1.Create(Application) do
    show;
end;

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if Application.MainForm <> nil
    then Params.WndParent:= Application.MainForm.Handle
    else Params.WndParent:= 0;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

end.

已经尝试过并且无法使用,这将导致所有次要窗体都能够到达主窗体的后面。 - Yona
实施您更新的代码后,程序启动时任务栏上会出现两个窗体。 - Yona

0

针对弹出式工具窗体的问题,当关闭按钮被按下时,应用程序会失去焦点。

解决方法: 在OnClose事件中使用Self.Hide

procedure TPopupForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Self.Hide;
  Action := caFree;
end;

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