Delphi - 如何防止Forms / MsgBox在先前的表单下移动?

时间:2010-06-08 12:06:19

标签: windows delphi dialog z-order delphi-6

在Windows 98时代之后,我们经历过多次对话失去了他们的Z-Order并回到之前的形式。

例如:

Dialog1.ShowModal;

Dialog1.OnClickButton() : ShowMessage('anything');

当MessageBox出现时,它有时没有焦点并在Dialog1下移动。 用户对此感到困惑,他们说:我的申请冻结了! 但如果他们使用Alt + Tab移动到另一个应用程序并返回,焦点将返回到MessageBox,它将成为前景窗口。

我们使用ShowMessage,MessageBox,普通表单以及QuickReport表单来体验这一点。

有人知道吗?这是一个Windows bug吗?你怎么能阻止它?怎么抓住这个?

感谢您的帮助:    DD


我真的说在Win98之后,所有操作系统(Win7也)都会受到这个问题的影响。 我们使用Delphi 6 Prof,因此属性不能使用默认表单。

有人说使用MessageBox + MB_APPLMODAL可以控制消息对话框。 这是个好消息,但我们有许多旧的表单和组件,第三方工具。

因此,用一个替换表格来创建一个全新的应用程序是很困难的。

但我们会尝试这样做。

我认为答案是这是半个应用程序问题和半个Windows问题。如果Windows有时会处理此问题,有时则不会 - 这似乎是一个Windows错误。 但是,如果我们可以强制制作好的模态窗口,那么它就是编程错误。

有人可以向我解释一下WS_POPUP标志的含义是什么? 是否有副作用?

感谢:   DD

4 个答案:

答案 0 :(得分:15)

这就是PopupMode and PopupParent properties的用途。

例如,你可以这样做:

Dialog1.PopupMode := pmExplicit;
Dialog1.PopupParent := self;
Dialog1.ShowModal;

这告诉Windows正确的Z顺序。

答案 1 :(得分:6)

对于旧版本的delphi(在Delphi 2007之前),在主窗体以外的表单上:

interface
  TMyForm = Class(TForm)
  protected
    procedure CreateParams(var Para: TCreateParams); override;
  end;
...
implementation
...
procedure TMyForm.CreateParams(var Para: TCreateParams);
begin
  inherited;
  Para.Style := Para.Style or WS_POPUP;
  { WinXP Window manager requires this for proper Z-Ordering }
  // Para.WndParent:=GetActiveWindow;
  Para.WndParent := Application.MainForm.Handle;
end;

对于消息框,请在标志中包含MB_TOPMOST:

Application.MessageBox(PChar(amessage), PChar(atitle),    otherflags or MB_TOPMOST);

答案 2 :(得分:0)

我查看了这个页面和常见问题解答半小时仍然无法找到如何发表评论,所以请原谅我违反协议。

首先,我想说清楚海报,恕我直言,不使用Windows 98.他写道“在Windows 98时代之后”我明白这意味着他在98版之后遇到了这个问题。

由于我也有这个问题(CB2009),我想强调一下海报的问题“它是Windows bug吗?”,我还没有看到答案。如果它是Delphi / Builder的bug,也许有办法避免它?我无法看到如何拦截所有可能的对话框是一个可行的解决方案,也不能避免使用fsStayOnTop。我有一个设置表单需要保持在我的主表单之上,但设置表单可以并且将弹出对话框,在特定条件下将在设置表单下消失。

如果我能理解z-order的支持在哪里出错将会非常有用,因为它可以提供如何避免它的线索。

答案 3 :(得分:0)

我最近使用的一个技巧是在创建每个表单时应用这两行代码:

SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or 
  WS_EX_APPWINDOW or WS_EX_TOPMOST);
SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopWindow);

句柄是表单的句柄(Form1.Handle)。 WS_EX_APPWINDOW部分使每个窗口出现在任务栏上,如果您不想要这种额外的效果,则将其删除。

对于我的主要表格,我使用这一行:

SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or
  WS_EX_TOPMOST);

我也使用这个函数来帮助构建我的自定义对话框(我为每种对话框创建了一个新函数 - 错误,确认等):

function CustomDlg(const AMessage : string; const ADlgType: TMsgDlgType;
  const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn) : TForm;
begin
  Result := CreateMessageDialog(AMessage, ADlgType, AButtons, ADefaultButton);
  with Result do
    begin
      SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or 
        WS_EX_APPWINDOW or WS_EX_TOPMOST);
      SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopwindow);
      FormStyle := fsStayOnTop;
      BringToFront;
    end;
end;

FormStyle := fsStayOnTop;部分当然是可选的,但我用它来确保我的确认和错误对话框始终对用户可见。

这似乎有些工作,但净效果是我不再需要担心形式意外地隐藏在其他形式之后。