关于对话框:如何避免FindDialog停留在顶部(Delphi)?

How Can I Keep the FindDialog from Staying on Top (Delphi)?

在Delphi 2009中,我做了一个简单的事情:

1
FindDialog.Execute;

"查找对话框"窗口应留在程序主窗口的顶部。

但是,如果我从自己程序的窗口中通过其他程序打开另一个窗口,则FindDialog窗口将保留在另一个窗口的顶部。

如果我尝试使用其他程序(例如记事本)中的FindDialog进行此操作,则不会发生这种情况。在记事本及其FindDialog上打开另一个程序的窗口将同时覆盖记事本和FindDialog窗口。这似乎是正确和预期的行为。

这是我做错了吗,还是Delphi实现FindDialog的方式有问题?我可以做些什么来使其以记事本方式工作吗?

谢谢大家的评论。您无法重现我的问题这一事实已经表明,这是导致此问题的其他原因。这将帮助我进行跟踪。我会进行更多研究,并在发现问题后在此处发布其他信息。

非常有趣。我的PrintDialog不会停留在最前面。仍然不知道为什么我的FindDialog会这样做。仍在研究...

我将调用更改为:FindDialog.Execute(Handle);仍在顶部。

我在主窗体中添加了另一个FindDialog(这次是FindDialog1),并在程序启动时执行它。它具有与众不同的行为。至少表明它与我的FindDialog或与此相关的自定义无关。因此,它必须是我的主要形式中的设置。

看起来我不是唯一遇到此问题的人。请参阅:资源调优器:版本历史,它似乎是一个Delphi应用程序,在1.99版中它指出:"错误修正:(搜索)对话框预览窗口在切换到另一个应用程序时位于顶部。"我可能会尝试与他们联系,并看看他们是否可能记得自己的解决办法。

我在表单中添加了一些新对话框,并将这些调用放在一个位置:

1
2
3
4
FindDialog1.Execute();
PrintDialog1.Execute();
ReplaceDialog1.Execute();
FontDialog1.Execute();

FindDialog和ReplaceDialog停留在其他窗口的顶部。
PrintDialog和FontDialog不会停留在顶部,无法正常工作。

那么使前两个对话框做错的两组对话框之间有何不同?

此外,在使用Delphi 4编译的我的程序的旧版本中也会发生此问题。糟糕。现在,我看到在使用Delphi 4的旧版本中没有发生此问题。

并且是一位用户报告了此问题。他使用Windows XP,并且我正在Vista上进行开发,因此它发生在不同的OS下。

确认:是的,我创建一个新表单并在其上添加一个FindDialog。 FindDialog没有问题。这表明我程序中的某些内容导致FindDialog停留在最前面。现在,我只是要找出那是什么。还有其他想法吗?如果有人给我一个答案,甚至给我一个线索来帮助我解决这个问题,那么他们将得到公认的答案。

解决方案:Sertac对他的答案的编辑为我提供了解决方法:

1
2
3
  Application.NormalizeTopMosts;
  FindDialog.Execute();
  Application.RestoreTopMosts;

这样做可以防止当应用程序不是TopMost时,FindDialog成为TopMost。

...但是我仍然真的不明白这一点(NormalizeTopMosts上的Delphi帮助)非常令人困惑,也并不表示它应该这样做。

希望此"修复"不会引起其他问题。


查看VCL代码,"查找对话框"位于顶部的唯一可能方法是,在调用"执行"时已经有一个最顶部的窗口。这就是它的编码方式,对话框由" TRedirectorWindow"拥有,该" TRedirectorWindow"由应用程序中z顺序的顶部窗口拥有。如果此"顶部窗口"是最顶部的窗口,则"查找"对话框也是。

1
2
3
4
5
6
7
8
9
procedure TForm1.Button1Click(Sender: TObject);
var
  f: TForm;
begin
  f := TForm.CreateNew(Self);
  f.FormStyle := fsStayOnTop;
  f.Show;
  FindDialog1.Execute;
end;

1
2
3
4
5
6
procedure TForm1.Button1Click(Sender: TObject);
begin
  FormStyle := fsStayOnTop;
  FindDialog1.Execute;
  FormStyle := fsNormal;
end;

以上示例将创建一个最顶部的查找对话框。但是,保持最新状态可能不会被忽略,所以我想这不会成为您问题的根源。

在任何情况下,要么是由于某种原因,要么是通过某种其他代码段更改了对话框上的样式。

顺便说一句,不要费心测试将各种句柄传递给FindDialog1.Execute(),它不会起作用,请参阅我对您问题的评论。

编辑:

这个怎么样:

1
2
3
4
5
6
7
8
9
10
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"执行的操作以查找对话框的基础,并且如果对话框最顶部,则会引发异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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版本中,此方法已损坏并且无法完成其工作。