如何防止 FindDialog 保持在最前面 (Delphi)?

发布于 2024-10-24 03:19:10 字数 1890 浏览 3 评论 0原文

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

FindDialog.Execute;

FindDialog 窗口按其应有的方式保留在程序主窗口的顶部。

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

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

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


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


非常有趣。我的 PrintDialog 没有保持在顶部。仍然不知道为什么我的 FindDialog 会这样做。仍在研究...


我将调用更改为:FindDialog.Execute(Handle);仍然在上面。


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


看来我不是唯一遇到这种情况的人。请参阅:Resource Tuner:版本历史记录,它似乎是一个 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 的帮助)非常令人困惑,并且并不表明它应该这样做。

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

In Delphi 2009, I do a simple:

FindDialog.Execute;

The FindDialog window stays on top of my program's main window as it should.

However, if I open another window from some other program over my own program's window, the FindDialog window remains on top of the other window.

If I try this with a FindDialog from another program (e.g. Notepad), this does not happen. Opening another program's window over Notepad and its FindDialog will cover both the Notepad and FindDialog windows. This seems to be the correct and expected behavior.

Is this something I'm doing wrong or is this a problem with the way Delphi has implemented the FindDialog? Is there something I can do to make it work the Notepad way?


Thank you all for the comments. The fact that you cannot reproduce my problem is already a clue that it is something else causing this. This will help me track it down. I'll research a little more and post additional info here when I find out something.


Very interesting. My PrintDialog doesn't stay on top. Still don't know why my FindDialog does. Still researching...


I changed the call to: FindDialog.Execute(Handle); Still on top.


I added another FindDialog (this time FindDialog1) to my main form and I execute it at startup of my program. It has the same stay-on-top behavior. That at least indicates it wasn't anything to do with my FindDialog or customizations I made to do with it. So it must be a setting in my main form.


It doesn't look like I'm the only one who's encountered this. See: Resource Tuner: Version History which appears to be a Delphi application, where under Version 1.99 it states: "Bugfix: The (search) dialog preview window stayed on top when switching to another application." I might try contacting them and see if they might remember what their fix was.


I add some new dialogs to my form and put these calls in one place:

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

The FindDialog and ReplaceDialog stay on top in front of other windows.
The PrintDialog and FontDialog do not stay on top and work as they should.

So what is different between the two sets of dialogs that make the first two do it wrong?


Also, this problem happens in an old version of my program that was compiled with Delphi 4. Whoops. Now I see this problem did not happen in my old version that used Delphi 4.

And it was a user who reported this problem. He uses Windows XP, and I'm developing on Vista, so it happens under different OS's.


Confirmation: Yes, I create a new form and add a FindDialog on it. The FindDialog does NOT have the problem. This indicates something in my program is causing the FindDialog to stay on top. Now, I've just got to find out what that is. Any more ideas? If someone gives me an answer that even gives me a clue to help me solve this, then they'll get the accepted answer.


Solution: Sertac's edit to his answer gave me the workaround:

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

Doing this prevents the FindDialog from being TopMost when the Application is not TopMost.

... But I still really don't understand this (the Delphi help on NormalizeTopMosts) is very confusing and doesn't indicate that it should do this.

Hopefully this "fix" won't cause other problems.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一刻暧昧 2024-10-31 03:19:10

查看 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 版本中,此方法已损坏并且无法完成其工作。

Looking at the VCL code, the only possible way a Find Dialog Box stays on top is, there's already a top-most window when 'Execute' is called. This is how it is coded, the dialog gets owned by a 'TRedirectorWindow' which gets owned by the top window in z-order in the application. If this 'top window' is a top-most window then the find dialog also is.

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

or,

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

The above samples will create a top-most find dialog. But a stay-on-top form possibly wouldn't go unnoticed, so I guess this wouldn't be the source of your problem.

In any case, it is either that or you're somehow changing the styles on the dialog by some other code piece.

BTW, do not bother testing passing various handles to FindDialog1.Execute(), it won't have an effect, see my comment to your question.

edit:

How about this one:

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

The point is, a window does not have to be visible to get enumerated by EnumThreadWindows. So any existing stay-on-top form could cause the find dialog to exhibit this behavior.

Better test and see than to guess. Run the below test just before you launch your Find Dialog. This incorporates the logic 'dialogs.pas' performs to find the dialog a base, and would raise an exception if the dialog would go top-most.

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;

One other test could be to call NormalizeTopMosts of the Application before launching the dialog, but I know with some Delphi versions this method is broken and does not do its job.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文