Delphi - как узнать, какой модальный диалог имеет фокус, и перенести его на передний план - PullRequest
7 голосов
/ 21 марта 2012

У меня есть приложение Delphi 2006, которое может вызвать модальное диалоговое окно в ответ на ошибку.Кажется, что он входит в состояние, когда один из этих модальных диалогов открыт, расположен перед основной формой, но ни одна форма не отвечает на сообщения.Нажатие на любой из них дает «бонк».Приложение работает нормально, пользовательский интерфейс обновляет основную форму, но вы ничего не можете сделать.Я предполагаю, что, скорее всего, есть другой модальный диалог под основной формой.Будь то мой или один из Windows, я понятия не имею.

Другие вопросы:

  • приложение реагирует на сочетания клавиш в порядке.Один из этих ярлыков корректно завершает работу приложения, и это сработало.С тех пор я не могу воспроизвести ситуацию.
  • приложение имеет иконку в трее.Это реагирует на щелчки правой кнопкой мыши.Если я сверну приложение отсюда, основная форма свернется и оставит модальное диалоговое окно отображенным, но без фокуса.Если я восстановлю основную форму, все будет так, как было, и ни у одного окна нет фокуса.Alt-Tab имеет аналогичные результаты.
  • платформа Windows 7
  • Я вызываю DisableProcessWindowsGhosting до того, как будут созданы какие-либо формы
  • Я открываю модальные диалоги с помощью

    ModalDialog.PopupParent := MainForm ;
    ModalDialog.ShowModal ;
    
  • Я откладываю эти диалоги ошибок, если открыты другие модальные диалоги:

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

Мой вопрос состоит из двух частей:

Есть ли способ программного поискаиз какого окна есть фокус?Затем я мог бы предпринять какое-то действие для этого сценария или в крайнем случае, я мог бы предоставить им комбинацию клавиш, чтобы вывести его на передний план, или предпринять какое-нибудь уклончивое действие (в зависимости от диалога), например установить для ModalResult значение mrCancel.

Как эта ситуация может возникнуть?Обычно, когда я получаю модальное диалоговое окно за главной формой (я могу сделать это, открыв модальное диалоговое окно, свернув приложение с помощью иконки в трее, а затем восстановив приложение снова - основная форма приложения восстанавливается перед диалоговым окном, сдиалоговое окно по-прежнему остается в фокусе), я могу снова вывести его на передний план, нажав на значок в трее, или закрыть его клавишей Esc, но в этом случае это не сработало.

** ОБНОВЛЕНИЕ **

Исправление Миши работало отдельно от не-дельфийских диалогов, таких как TSaveDialog.Я смог заставить их работать также, добавив Application.ModalPopupMode := pmAuto ; непосредственно перед вызовом Execute.

Под словом «заставил его работать» я имею в виду, что диалоговое окно сохранения было впереди после следующей последовательности:

  • открыть диалоговое окно сохранения
  • свернуть приложение из иконки в трее
  • восстановить приложение из иконки в трее

, тогда как оно было позади основногоформа без ModalPopupMode := pmAuto.

Так что я надеюсь, что эти изменения помогут (пока не воспроизведена) проблема.

Ответы [ 4 ]

5 голосов
/ 22 марта 2012

Если форма с фокусом занимает слишком много времени для ответа на сообщения (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;
4 голосов
/ 22 марта 2012

Последнее активное всплывающее окно (VCL или нет) может быть запрошено с помощью GetLastActivePopup:

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 может решить проблему целиком, но однажды я почувствовал, что она не работает должным образом, хотя с тех пор я не смог воспроизвести ситуацию.

0 голосов
/ 22 октября 2012

Я использовал решение Миши и немного поработал (используя код 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;
0 голосов
/ 22 марта 2012

GetForegroundWindow () - это функция, которую вы ищете, если вы знаете заголовок или имеете дескриптор модального окна, это просто.

HWND GetForegroundWindow ();

Извлекает дескриптор окна переднего плана (окна, с которым в данный момент работает пользователь).Система назначает несколько более высокий приоритет потоку, который создает окно переднего плана, чем другим потокам.

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

...