【问题标题】:How Can I Keep the FindDialog from Staying on Top (Delphi)?我怎样才能让 FindDialog 保持在顶部(Delphi)?
【发布时间】:2011-03-21 04:52:46
【问题描述】:

在 Delphi 2009 中,我做了一个简单的操作:

FindDialog.Execute;

FindDialog 窗口应保持在程序主窗口的顶部。

但是,如果我在自己的程序窗口上从其他程序打开另一个窗口,则 FindDialog 窗口仍位于另一个窗口的顶部。

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

这是我做错了什么还是 Delphi 实现 FindDialog 的方式有问题?有什么办法可以让它以记事本的方式工作吗?


感谢大家的cmets。您无法重现我的问题这一事实已经表明这是其他原因造成的。这将帮助我追踪它。当我发现一些东西时,我会进行更多研究并在此处发布更多信息。


非常有趣。我的 PrintDialog 没有保持在顶部。仍然不知道为什么我的 FindDialog 会。还在研究中……


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


我在主窗体中添加了另一个 FindDialog(这次是 FindDialog1),并在程序启动时执行它。它具有相同的保持领先的行为。这至少表明它与我的 FindDialog 或我所做的自定义无关。所以它一定是我主窗体中的一个设置。


看起来我不是唯一遇到这种情况的人。请参阅:Resource Tuner: Version History,它似乎是一个 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;

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

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

希望这个“修复”不会导致其他问题。

【问题讨论】:

  • 无法重现此(D2009 完全更新)。使用了FindDialog1.Execute;FindDialog1.Execute();,这都应该导致没有传入句柄。当我打开“查找”对话框时,然后打开其他一些应用程序(在我的例子中是记事本)并将它移动到我的 Delphi 应用程序上,这两个都是它的主要窗口和查找对话框被记事本的窗口覆盖。
  • 我可能在这里误解了一些东西,但我无法在 Delphi XE 中重现。
  • FWIW 我无法在 D2010 中重现此行为。您是否将 HWND 传递给 Execute 方法?如果没有尝试传递主窗体的句柄,看看是否有帮助。
  • 请发布一个小的编译示例来演示此异常。
  • 您是否使用了接受窗口句柄作为参数的重载 FindDialog.Execute?如果是这样,你传递的是什么句柄?如果您传递的不是表单的句柄(创建它的表单),这可能是问题所在。如果您现在什么都不传递,请尝试传递您的 MainForm.Handle。

标签: delphi dialog stayontop


【解决方案1】:

查看 VCL 代码,查找对话框保持在顶部的唯一可能方式是,调用“执行”时已经有一个最顶部的窗口。这就是它的编码方式,对话框归“TRedirectorWindow”所有,该“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 版本中,此方法已损坏并且无法正常工作。

【讨论】:

  • 嗯。我确实有 3 个表单,它们是 FormStyle := fsStayOnTop,但这些是特殊功能表单(如我的关于框),当此问题发生时尚未打开。不过,你的回答让我思考。谢谢。
  • NormalizeTopMosts 似乎为我解决了这个问题。请参阅我编辑的答案。谢谢 Sertac!
  • @lkessler - 不客气!我不认为'NormalizeTopMosts' 会导致任何问题,标准化将持续一个非常小的时间范围......,不要忘记在'finally' 中保护'RestoreTopMosts'。对于生产应用程序,我可能会修改 dialogs.pas 以摆脱无意义的代码并使重载工作,但当然,这可能根本不可行(包、公司政策等)。
  • 参见beholdgenealogy.com/blog/?p=812上的“在不知道是什么原因的情况下解决错误”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-22
  • 2014-01-02
  • 2017-02-20
  • 2019-09-16
相关资源
最近更新 更多