Delphi 调用ShowModal时,表单隐藏在其他表单后面

Delphi 调用ShowModal时,表单隐藏在其他表单后面,delphi,delphi-7,Delphi,Delphi 7,我的申请是基于情态形式的。主窗体使用showmodel打开一个窗体,此窗体使用showmodel打开另一个窗体,因此我们有堆叠的模态窗体。有时会出现这样一个问题:当我们以新的形式调用ShowModal时,它隐藏在以前的形式后面,而不是显示在顶部。按alt+tab后,表单返回顶部,但这不是一个好的解决方案。你遇到这个问题了吗?你是如何处理的 编辑: 我使用Delphi7。我发现在多个表单上使用“始终在顶部”标志会导致Z顺序出现问题。您也可能会发现对该功能的需求 使用内置WinAPI(Message

我的申请是基于情态形式的。主窗体使用showmodel打开一个窗体,此窗体使用showmodel打开另一个窗体,因此我们有堆叠的模态窗体。有时会出现这样一个问题:当我们以新的形式调用ShowModal时,它隐藏在以前的形式后面,而不是显示在顶部。按alt+tab后,表单返回顶部,但这不是一个好的解决方案。你遇到这个问题了吗?你是如何处理的

编辑


我使用Delphi7。

我发现在多个表单上使用“始终在顶部”标志会导致Z顺序出现问题。您也可能会发现对该功能的需求


使用内置WinAPI(
MessageBox
)启动消息框时,我发现传递调用窗口的句柄是必要的,以确保提示始终显示在顶部。

您没有提到哪个版本的Delphi

较新的Delphi版本向TCustomForm添加了两个新属性:PopupMode和PopupParent。将模态对话框的POUPPARENT设置为创建该对话框的窗体可确保子窗体位于其父窗体的顶部。它通常会解决您描述的问题

我认为这对属性是在Delphi 2006中添加的,但可能是在2005年。他们肯定在德尔福2007年及以上

编辑:在看到您正在使用Delphi7之后,我唯一的建议是,在显示模态表单的代码中,禁用创建它的表单,并在返回时重新启用。这将阻止创建窗口接收输入,这可能有助于保持Z顺序正确

类似的东西可能会起作用(未经测试,因为我不再使用D7):

如果Form2创建了一个模式窗口(正如您所提到的),只需重复这个过程-禁用Form2,创建Form3并以模式方式显示它,然后在它返回时重新启用Form2。确保使用try..finally,如我所示,这样,如果模态表单出现问题,创建表单总是重新启用。

由此看来,问题在于2000/XP中引入的“重影窗口”。您可以通过在启动时调用以下代码来禁用重影功能

procedure DisableProcessWindowsGhosting;
var
  DisableProcessWindowsGhostingProc: procedure;
begin
  DisableProcessWindowsGhostingProc := GetProcAddress(
    GetModuleHandle('user32.dll'),
    'DisableProcessWindowsGhosting');
  if Assigned(DisableProcessWindowsGhostingProc) then
    DisableProcessWindowsGhostingProc;
end; 

我能看到的唯一问题是,它将导致允许用户使用的功能出现问题。但是通过这种方式,您不必用
Self.Enabled:=False
code来覆盖每个调用。

很抱歉添加了一个单独的答案,但我做了更多的研究,其中一些研究表明我以前的答案(DisableProcessWindowsGhosting)没有帮助。因为我不能总是重复这个问题,我不能肯定

我找到了一个似乎合适的解决方案。我在Delphi2007中引用了CreateParams方法的代码,它与之非常匹配(没有处理PopupMode的所有其他代码)

我创建了子类
TForm
下面的单元

unit uModalForms;

interface

uses Forms, Controls, Windows;
type
  TModalForm = class(TForm)
  protected
    procedure CreateParams(var params: TCreateParams); override;
  end;

implementation

procedure TModalForm.CreateParams(var params: TCreateParams);
begin
  inherited;

  params.WndParent := Screen.ActiveForm.Handle;

  if (params.WndParent <> 0) and (IsIconic(params.WndParent)
    or not IsWindowVisible(params.WndParent)
    or not IsWindowEnabled(params.WndParent)) then
    params.WndParent := 0;

  if params.WndParent = 0 then
    params.WndParent := Application.Handle;
end;
unitumodalforms;
接口
使用窗体、控件和窗口;
类型
TModalForm=类(TForm)
受保护的
过程CreateParams(变量参数:TCreateParams);推翻
结束;
实施
过程TModalForm.CreateParams(变量参数:TCreateParams);
开始
继承;
params.WndParent:=Screen.ActiveForm.Handle;
if(参数WndParent 0)和(IsIconic(参数WndParent)
或不可见(参数WndParent)
或者不启用IsWindowEnabled(params.WndParent)),然后
params.WndParent:=0;
如果params.WndParent=0,则
params.WndParent:=Application.Handle;
结束;
然后我要做的是将这个单元包含在表单单元中,然后将表单的类(在.pas代码文件中)从
class(TForm)
更改为
class(TModalForm)


它对我有效,似乎接近CodeGear的解决方案。

只需将要打开的表单的
属性设置为
False
。然后您可以使用
.showmodel()打开它,它就会工作。

试试看 网上展示:

PostMessage(Self.Handle, WM_USER_SET_FOCUS_AT_START, 0, 0);

对不起,这里是德尔福7。没有PopupMode和PopupParent,但是知道它们的存在是好的。我可以尝试这个解决方案,但是我们在项目中有很多模态表单,消息框是模态的,所以有时候可能很难实现。但我会尽可能做到这一点。使用Self.Enabled:=False对我有效;和Self.Enabled:=True;我可以确认Popupprent解决方案在Delphi XE2中非常有效,非常感谢@邓肯:如果我的回答对你有帮助的话,你可以通过投票来表达你的谢意。:-)请参阅我对Lars D的回复。所有者(或家长)与所描述的问题无关。问题是Tapplication的隐藏窗口被从任务栏中移除后产生的后遗症;它导致了子窗体在Z顺序中丢失正确位置的问题,而创建PopupParent和PopupMode就是为了解决这个问题。。。进一步检查后,我意识到我正在考虑使用Windows.MessageBox函数并传递调用窗口的句柄,以确保MessageBox显示在调用方的顶部,我认为调用方是“父对象”。将编辑我的答案以反映这一点。感谢添加版本。如果您将其包含在问题的文本或标记的某个位置,则会有所帮助。:-)我确实先设置了正确的标记,然后添加了编辑:)只要你把它放在某个地方,它就会工作。它不一定要在某个标签中,只要它在问题的主题或文本的某个地方。这让人们在回答问题时知道您可以使用哪些功能。:-)我们在第三方应用程序上已经有这个问题很多年了,现在还没有找到修复程序。如果它真的有效,那就太好了。非常感谢。这个问题困扰了我很长时间。然而,让我感兴趣的是,如果表单具有WS_弹出式样式,并且“所有者”是正确的窗口,那么即使是“重影”表单也是不允许的
PostMessage(Self.Handle, WM_USER_SET_FOCUS_AT_START, 0, 0);