德尔福 - 我如何找出哪个模态对话框具有焦点并将其带到前面?

时间:2012-03-21 19:59:20

标签: delphi delphi-2006

我有一个Delphi 2006应用程序,它可以弹出一个模态对话框以响应错误情况。它似乎进入一种状态,其中一个模态对话框是打开的,位于主窗体的前面,但两种形式都没有响应消息。点击其中一个可以得到一个" bonk"。应用程序运行正常,UI正在更新主表单,但您无法执行任何操作。我想主要形式下很可能还有另一个模态对话框。无论是我的还是Windows的我都不知道。

其他要点:

  • 应用响应键盘快捷键确定。其中一个短片优雅地关闭了应用程序,这很有效。从那以后我一直无法重现这种情况。
  • 该应用有一个托盘图标。这会响应鼠标右键单击。如果我从这里最小化应用程序,主窗体最小化并显示模式对话框,仍然没有焦点。如果我恢复主窗体,事情就像它们一样,两个窗口都没有焦点。 Alt-tab也有类似的结果。
  • 平台是Windows 7
  • 在创建任何表单之前调用DisableProcessWindowsGhosting
  • 我用

    打开模态对话框
    ModalDialog.PopupParent := MainForm ;
    ModalDialog.ShowModal ;
    
  • 如果其他模态对话框打开,我会推迟这些错误对话框:

    if (Application.ModalLevel = 0) then
        {open modal dialog}
    

我的问题分为两部分:

有没有办法以编程方式找出哪个窗口有焦点?然后,我可以为这个场景或最后的手段采取一些行动,我可以提供一个快捷键将其置于前面或采取一些规避行动(取决于对话框),例如将ModalResult设置为mrCancel。

这种情况怎么会出现?通常当我在主窗体后面得到一个模态对话框时(我可以通过打开模态对话框,从托盘图标中最小化应用程序,然后再次恢复应用程序) - 应用程序主窗体在对话框前面恢复,对话框仍保留焦点),我可以通过单击托盘图标将其再次显示在前面,或者使用Esc键将其关闭,但在这种情况下它不起作用。

**更新* *

Misha的修复与TSaveDialog之类的非delphi对话分开了。我可以在调用Application.ModalPopupMode := pmAuto ;之前添加Execute来让他们工作。

通过"让它工作"我的意思是在以下序列之后保存对话框在前面:

  • 打开保存对话框
  • 从托盘图标
  • 最小化应用
  • 从托盘图标恢复应用

虽然它不在ModalPopupMode := pmAuto的主窗体后面。

因此,我希望这些变化有助于(尚未产生)问题。

4 个答案:

答案 0 :(得分:5)

如果具有焦点的表单花费太长时间来响应消息(Form1),那么Windows认为Form1没有响应,然后Form1显示模式表单(Form2),在显示Form2并且应用程序再次处理消息之后,Form1将被带到前面,从而可能“覆盖”Form2。

将它放在Application.OnIdle事件中可以解决这个问题:

  if Assigned(Screen.ActiveForm) then
  begin
    if (fsModal in Screen.ActiveForm.FormState) and
       (Application.DialogHandle <= 0)) then 
    begin
      Screen.ActiveForm.BringToFront;
    end;
  end;

答案 1 :(得分:4)

可以使用GetLastActivePopup查询最后一个活动弹出窗口(是否为VCL):

function GetTopWindow: HWND;
begin
  Result := GetLastActivePopup(Application.Handle);
  if (Result = 0) or (Result = Application.Handle) or
      not IsWindowVisible(Result) then
    Result := Screen.ActiveCustomForm.Handle;
end;

这有点复制自TApplication.BringToFront

将此窗口置于前面可以通过SetForegroundWindow

完成
SetForegroundWindow(GetTopWindow);

请注意,Application.BringToFront可能会完全解决这个问题,但我曾经历过它无法正常运行,这是我无法重现的情况。

答案 2 :(得分:0)

GetForegroundWindow()是您正在寻找的功能,如果您知道标题或具有模态窗口的句柄,那么它很简单。

HWND GetForegroundWindow();

  

检索前景窗口的句柄(用于显示窗口的窗口)   用户目前正在工作)。系统分配稍高   创建前景窗口的线程的优先级比它的优先级   到其他线程。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633505%28v=vs.85%29.aspx

答案 3 :(得分:0)

我使用Misha的解决方案并进一步工作(使用NGLN的代码)来解决rossmcm看到的问题(处理非VCL对话框)。

以下代码正在运行我的计时器:

type
  TCustomFormAccess = class(TCustomForm);


if Assigned(Screen.ActiveCustomForm) then
begin
  if ((fsModal in Screen.ActiveCustomForm.FormState) and
      (Application.DialogHandle <= 0)) then
  begin
    TopWindow := GetLastActivePopup(Application.Handle);
    TopWindowForm := nil;
    for i := 0 to Screen.CustomFormCount - 1 do
    begin
      CustomFormAccess := TCustomFormAccess(Screen.CustomForms[i]);
      if CustomFormAccess.WindowHandle = TopWindow then TopWindowForm := CustomFormAccess;
    end;
    if Assigned(TopWindowForm) and (Screen.ActiveCustomForm.Handle <> TopWindow) then
    begin
      Screen.ActiveCustomForm.BringToFront;
    end;
  end;
end;