当应用程序最小化时隐藏表单

8
我有一个主窗体和一个状态窗体,当应用程序正在运行时我会显示它。如果工作完成,我只需在状态窗体上调用Hide,状态窗体就会消失。
我的问题出现在当等待窗体可见时我最小化主窗体。这时两个窗体都被隐藏了,这正是我想要的结果。然而,如果当主窗体最小化时工作完成,那么当我将其还原时,状态窗体也会还原,即使在最小化时已经调用了Hide
当应用程序最小化时,状态窗体的Visible属性似乎为False,因此调用Hide似乎没有效果(帮助文档说它只是将Visible设置为False)。
这些观察结果是否正确?当应用程序再次获得焦点时,窗体可见性如何恢复?如何在应用程序最小化时隐藏我的窗体?

1
在此显示窗体中覆盖“CreateParams”中的“WndParent”吗? - Sertac Akyuz
+1听起来像是你对窗口所有权做了一些非标准的操作。 - David Heffernan
那么有多少个表单,它们中的任何一个是模态的吗? - David Heffernan
不需要模态窗体来重现此操作。只需主窗体和状态窗体即可。状态窗体设置了 fsStayOnTop。当状态窗体可见时,使用两个窗口消息禁用主窗体控件。 - jpfollenius
1
我现在终于理解了问题,并且已经编辑了问题,希望能更清楚明白。 - David Heffernan
显示剩余2条评论
3个回答

4

Visible 属性为 false 时,调用 Hide 方法并不能使应用程序最小化的显示表单消失,因为它在最小化过程中被应用程序隐藏了。

代码在应用程序最小化时先使用参数 'False' 调用ShowOwnedPopups函数,然后在应用程序还原时使用参数 'True' 调用该函数。由于该函数会显示上一个调用所隐藏的所有窗口,因此在中间改变窗体可见性不会有任何效果。

现在,查看该函数文档“注释”部分的以下引用:

如果使用 ShowWindow 函数隐藏弹出窗口,则随后使用 fShow 参数设置为 TRUE 的 ShowOwnedPopups 不会导致窗口显示

因此,一种解决方案是在应用程序隐藏窗体之前隐藏它,这样在还原时就不会被显示出来。但是这会带来一个问题,我们必须知道当我们还原时是否要显示该显示表单。这可以通过将属性放在显示表单上或者使用全局变量来实现。在下面的示例中,“ShouldBeVisible” 是一个假想的属性,如果我们要显示信息,则该属性将返回 true:

type
  TForm1 = class(TForm)
  ..
  private
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  ...

procedure TForm1.WMSysCommand(var Msg: TWMSysCommand);
begin
  if (Msg.CmdType = SC_MINIMIZE) and Assigned(Form2) and Form2.Visible then
    Form2.Hide;
  inherited;
  if (Msg.CmdType = SC_RESTORE) and Assigned(Form2) and Form2.ShouldBeVisible then
    Form2.Show;
end;

+1 非常好的想法。一个问题:只有主窗体接收这些事件吗?我正在多个表单中使用它们,因此希望将机制集成到状态表单本身中。但是似乎该表单并未接收这些事件。 - jpfollenius
刚刚尝试在主窗体中捕获事件。不幸的是,当我在Windows 7上将窗口恢复时,我的主窗体没有触发SC_RESTORE - jpfollenius
@Smasher - 我觉得有点奇怪,窗口应该在响应WM_SYSCOMMAND时被恢复。我想知道窗口是否接收到一个包含SC_RESTORE变体的消息。你能否像WM_SYSCOMMAND中的备注部分所解释的那样,将消息的wParam与$FFF0进行“按位与”操作?'ApplicationEvents'的'OnRestore'事件会起作用吗? - Sertac Akyuz
是的,Application.OnRestore 的效果符合预期。如果您在 Windows 7 上单击任务栏图标(这实际上也是最小化),则 SC_MINIMIZE 不会触发。请参见我的答案以获取适用于我个人的解决方案。感谢您的帮助! - jpfollenius

3
我现在使用以下解决方案,对我有效:
  1. Application.OnRestore恢复事件处理程序中,我调用StatusForm.NotifyRestored。如果不需要显示状态表单,则显式隐藏状态表单。
  2. 在我的状态表单中,我通过布尔字段FShouldDisplay跟踪可见性。这是在ShowStatusFormHideStatusForm方法中设置的。

procedure TMainForm.OnApplicationRestore(Sender : TObject);
begin
StatusForm.NotifyRestored;
end;

procedure TStatusForm.NotifyRestored;
begin
if not FShouldDisplay then
  ShowWindow(Handle, SW_HIDE);
end;

procedure TStatusForm.ShowStatusForm;
begin
FShouldDisplay := True;
Show;
end;

procedure TStatusForm.HideStatusForm;
begin
FShouldDisplay := False;
Hide;
end;

既然您使用了ApplicationEvents组件解决了问题,我想知道该组件是否可以在状态表单本身上工作。 - Sertac Akyuz
@Sertac:你说得对,那样可以省去委托部分。谢谢,我会尝试的。 - jpfollenius

2

警告:我不能百分之百确定以下方法是安全的。

如果您不需要相同的表单对象在应用程序的生命周期内保持活动状态(您很可能不需要),则可以尝试禁用弹出表单的自动创建(项目/选项),然后通过以下方式创建和显示它

Application.CreateForm(TForm2, Form2);
Form2.Show;

然后通过释放它来解除它的绑定

Form2.Release;

这样,表单就无法与主表单一起恢复。

我不会说这是唯一正确的做法,但绝对是相当普遍的一种。至少,在我们的项目中,大多数弹出窗口都是这样处理的。 - Andriy M
谢谢!由于这个对话框经常出现(大多数时间都是相当短的间隔),我更喜欢只创建一次表单。 - jpfollenius
也许状态窗口可以成为您主窗体的子控件?(换句话说,将“Form2”状态窗口替换为主窗体上的“TPanel”控件。) - Andreas Rejbrand
主窗体不是唯一需要此表单的窗体。可能是显示它的模态对话框或非模态编辑器窗口。 - jpfollenius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接