Как я могу не дать FindDialog оставаться на вершине (Delphi)?

В Delphi 2009 я делаю простое:

FindDialog.Execute;

Окно FindDialog остается поверх главного окна моей программы, как и должно быть.

Однако, если я открою другое окно из какой-либо другой программы поверх окна моей собственной программы, окно FindDialog останется поверх другого окна.

Если я попробую это с FindDialog из другой программы (например, Блокнот), это не произойдет. Открытие другого окна программы через Блокнот и его FindDialog покроет оба окна: Блокнот и FindDialog. Кажется, это правильное и ожидаемое поведение.

Это то, что я делаю неправильно, или это проблема с тем, как Delphi реализовал FindDialog? Есть ли что-то, что я могу сделать, чтобы это работало в блокноте?


Спасибо всем за комментарии. Тот факт, что вы не можете воспроизвести мою проблему, уже указывает на то, что это вызвано чем-то другим. Это поможет мне отследить это. Я исследую немного больше и опубликую дополнительную информацию здесь, когда узнаю что-то.


Очень интересно. Мой PrintDialog не остается на вершине. До сих пор не знаю, почему мой FindDialog делает. Все еще исследую...


Я изменил вызов на: FindDialog.Execute(Handle); Все еще на вершине.


Я добавил еще один FindDialog (на этот раз FindDialog1) в свою основную форму и запускаю ее при запуске моей программы. У него такое же поведение "остаться на вершине". Это по крайней мере означает, что это не имеет ничего общего с моим FindDialog или настройками, которые я сделал с ним. Так что это должна быть настройка в моей основной форме.


Не похоже, что я единственный, кто столкнулся с этим. См. Ресурсный тюнер: История версий, которая выглядит как приложение Delphi, где в версии 1.99 говорится: "Исправление: окно предварительного просмотра (поиска) диалогового окна оставалось сверху при переключении на другое приложение". Я мог бы попытаться связаться с ними и посмотреть, помнят ли они, каково их решение.


Я добавляю несколько новых диалогов в форму и помещаю эти вызовы в одном месте:

FindDialog1.Execute();
PrintDialog1.Execute();
ReplaceDialog1.Execute();
FontDialog1.Execute();

FindDialog и ReplaceDialog остаются поверх других окон. PrintDialog и FontDialog не остаются на вершине и работают, как они должны.

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


Кроме того, эта проблема возникает в старой версии моей программы, которая была скомпилирована с Delphi 4. К сожалению. Теперь я вижу, что этой проблемы не было в моей старой версии, которая использовала Delphi 4.

И это был пользователь, который сообщил об этой проблеме. Он использует Windows XP, а я занимаюсь разработкой под Vista, поэтому это происходит под разными ОС.


Подтверждение: да, я создаю новую форму и добавляю в нее FindDialog. FindDialog не имеет проблемы. Это указывает на то, что что-то в моей программе заставляет FindDialog оставаться на вершине. Теперь я просто должен выяснить, что это такое. Есть еще идеи? Если кто-то даст мне ответ, который даже даст мне подсказку, чтобы помочь мне решить эту проблему, то он получит принятый ответ.


Решение: редактирование Sertac его ответа дало мне обходной путь:

  Application.NormalizeTopMosts;
  FindDialog.Execute();
  Application.RestoreTopMosts;

Это не позволяет FindDialog быть TopMost, когда приложение не является TopMost.

... Но я все еще не понимаю этого (справка Delphi по NormalizeTopMosts) очень запутанная и не означает, что она должна это делать.

Надеюсь, это "исправление" не вызовет других проблем.

1 ответ

Решение

Глядя на код VCL, единственный возможный способ, которым диалоговое окно поиска остается сверху, - это уже самое верхнее окно, когда вызывается "Выполнить". Вот как это закодировано, диалоговое окно становится владельцем "TRedirectorWindow", которое становится владельцем верхнего окна в z-порядке в приложении. Если это "верхнее окно" является самым верхним окном, тогда диалог поиска также есть.

procedure TForm1.Button1Click(Sender: TObject);
var
  f: TForm;
begin
  f := TForm.CreateNew(Self);
  f.FormStyle := fsStayOnTop;
  f.Show;
  FindDialog1.Execute;
end;

или же,

procedure TForm1.Button1Click(Sender: TObject);
begin
  FormStyle := fsStayOnTop;
  FindDialog1.Execute;
  FormStyle := fsNormal;
end;


Приведенные выше примеры создадут самый верхний диалог поиска. Но форма "остаться на вершине", возможно, не останется незамеченной, поэтому я думаю, что это не будет источником вашей проблемы.

В любом случае, это либо так, либо вы каким-то образом меняете стили в диалоговом окне другим фрагментом кода.


Кстати, не мешайте тестированию, передавая различные ручки FindDialog1.Execute(), это не будет иметь эффекта, смотрите мой комментарий к вашему вопросу.

редактировать:

Как насчет этого:

procedure TForm1.Button4Click(Sender: TObject);
var
  f: TForm;
begin
  f := TForm.CreateNew(Self);
  f.FormStyle := fsStayOnTop;
  f.Show;
  f.Hide;
  FindDialog1.Execute;
end;

Дело в том, что окно не должно быть видимым для перечисления EnumThreadWindows, Таким образом, любая существующая форма "оставайся сверху" может привести к тому, что диалог поиска будет демонстрировать это поведение.

Лучше проверить и увидеть, чем угадать. Запустите приведенный ниже тест непосредственно перед тем, как запустить диалог поиска. Это включает в себя логику 'dialogs.pas', выполняющую поиск базы для диалога, и вызвала бы исключение, если бы диалог стал самым верхним.

function EnumThreadWndProc(hwnd: HWND; var lParam: LPARAM): Bool; stdcall;
var
  Window: TWinControl;
begin
  Result := True;
  Window := FindControl(hwnd);
  if Assigned(Window) and (Window is TForm) then begin
    Result := False;
    lParam := Longint(Window);
  end;
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  OnTopForm: Longint;
begin
  OnTopForm := 0;
  EnumThreadWindows(GetCurrentThreadId, @EnumThreadWndProc, LPARAM(@OnTopForm));
//  if (OnTopForm <> 0) and (TForm(OnTopForm).FormStyle = fsStayOnTop) then
  if (OnTopForm <> 0) and (GetWindowLong(TForm(OnTopForm).Handle,
                            GWL_EXSTYLE) and WS_EX_TOPMOST = WS_EX_TOPMOST) then
    raise Exception.Create('darn! got one: ' + TForm(OnTopForm).Name);
end;


Еще один тест может быть позвонить NormalizeTopMosts приложения до запуска диалога, но я знаю, что с некоторыми версиями Delphi этот метод не работает и не выполняет свою работу.

Другие вопросы по тегам