【问题标题】:Delphi, how to make independent windowsDelphi,如何制作独立的窗口
【发布时间】:2010-04-11 19:23:21
【问题描述】:

我有一个使用 Chrome 浏览器等标签的应用程序。现在我希望能够打开更多的表单,而不是仅限于一个表单。这些表单的行为应该相同,但如果我关闭主表单,所有表单都将关闭。我怎样才能使所有表单都相等,所以无论我关闭哪个表单,它只会关闭该表单而不是在所有表单关闭之前退出应用程序?有什么想法吗?

Image of my explorer

亲切的问候 罗伊·M·克莱弗

【问题讨论】:

  • 带注释的屏幕截图的链接可能有助于改善这个问题。我不确定我是否遵循表单、选项卡、主表单等描述。
  • 添加了图片链接,不需要cmets和描述。我只是希望能够打开更多这样的寡妇/表格。

标签: delphi user-interface forms tabs


【解决方案1】:

做到这一点并不难,但它会很快变得复杂,具体取决于您希望它有多完整。让多个模态对话框独立工作是一项艰巨的工作。

首先,您需要完全避免 Application.MainForm。始终使用Form := TMyForm.Create(Application) 而不是Application.CreateForm(TMyForm, Form)。后者设置 MainForm,您永远不希望发生这种情况。

要使事情正常关闭,您需要在表单的 OnClose 事件处理程序中执行类似的操作:

if Screen.FormCount = 1 then
  Application.Terminate;
CloseAction := caFree;

Application.Run 依赖于分配的 MainForm,因此在您的 DPR 中用此循环替换该行:

repeat
  try
    Application.HandleMessage;
  except
    Application.HandleException(Application);
  end;
until Application.Terminated;

有几种方法可以处理任务栏条目。

  1. 单个任务栏条目:设置Application.MainFormOnTaskbar := False;,将使用隐藏的 TApplication 句柄。单击任务栏条目会将所有窗口置于前面。您需要覆盖Application.OnMessage 或添加TApplicationEvents 组件,并使用Msg.Handle = Application.Handle 观察WM_CLOSE。在这种情况下,用户已右键单击任务栏并选择了关闭,因此您应该关闭所有窗口。

  2. 多个任务栏条目:设置Application.MainFormOntaskbar := True。覆盖表单的CreateParams 方法并设置Params.WndParent := 0;。每个任务栏条目都将控制该表单。

可能还有一些其他的陷阱,但这是基础知识。


正如我所说,让ShowModalTOpenDialog/TSaveDialog 独立工作,所以它只会影响其父窗体,因此可以同时打开多个对话框,这是一项繁重的工作,我真的不能推荐它。如果您是受虐狂,以下是一般步骤:

  1. TCustomForm.ShowModal 替换为自定义版本。除此之外,该例程会禁用应用程序中的所有其他窗口,因此您需要将 DisableTaskWindows/EnableTaskWindows 调用替换为 EnableWindow(Owner.Handle, False/True) 以禁用父窗体。此时您可以打开多个对话框,但它们只能以后进先出的顺序关闭,因为调用最终是递归的。如果没问题,就到此为止。

    有两种方法可以解决这个问题:

    1. 不要让ShowModal 阻塞,而是让StartModalEndModal 例程具有ShowModal 代码的第一位和最后一位,并在对话框关闭时调用OnShowModalDone 事件。这使用起来有点麻烦,但相对容易编码和稳定。

    2. 使用 Windows fiber routines 换出堆栈并开始新的消息循环。这种方法很容易使用,因为ShowModal 是阻塞的,所以你像往常一样调用它。这是我们在 Beyond Compare 中使用的方法。 不要这样做。编写起来很复杂,由于与第三方代码(Windows 全局消息挂钩、TWebBrowser、 .NET 在浏览对话框加载的 shell 扩展中等),如果它是一个跨平台项目,则 Unix ucontext 函数也不安全。

  2. 常见的对话框(TOpenDialog、TColorDialog 等)也有类似的限制。要使它们只禁用父表单,您需要覆盖 TCommonDialog.TaskModalDialog 并替换那里的 DisableTaskWindows/EnableTaskWindows 调用。但是,它们不能像上面的常规 Delphi 对话框那样异步,因为它们阻塞了 Windows 提供的功能(GetOpenFileNameChooseColor 等)。允许以任何顺序关闭的唯一方法是让每个对话框在专用线程中运行。 Windows 可以处理大部分同步操作,只要您小心访问 VCL 对象,但它基本上涉及重写大部分 Dialogs.pas

【讨论】:

  • 克雷格,谢谢你的回答。我可以看到它比我预期的要复杂一些。也许是时候多思考一下了。这对我来说是很好的帮助。感谢您抽出宝贵时间进行如此彻底的解释。我的问题将是模态对话框,我以前做过一些,但不是在这个设置中。回到思考框。
  • +1 用于描述功能齐全的解决方案。但是,有一种方法可以保留 Application.MainForm 并且不需要 ShowModal 和常见对话框的其他解决方法;见this answer
  • @NGLN:我掩盖了一些细节,我认为你误解了它试图做什么。每个窗口都是完全独立的,因此在一个窗口中做某事不应影响其他窗口。如果 MainForm 被分配关闭,它将终止应用程序。如果您使用 ShowModal 和没有变通办法的通用对话框,则所有其他窗口都将被禁用。即使您解决了这个问题,您也只能按照打开它们的相反顺序关闭它们。完整的实现支持同时在任意数量的顶级表单上打开模式对话框,并以任意顺序关闭它们。
  • 如果模式行为不是问题:只需对用户隐藏 Mainform,并实例化其他表单以供用户交互。最后一次关闭表单也可能关闭 Mainform。
【解决方案2】:

如果你真的想要,

1) 使用一个小的,可能是隐藏的 MainForm,并在启动时启动第一个子窗体。

2) 在同一进程中启动单独的应用程序而不是 Windows。这是以后的 Office 版本使用的。

【讨论】:

  • 您好 Henk,老实说,我正在研究 2 号解决方案,只需要找到一种在单独的应用程序之间同步数据的方法。正如 Craig 指出的那样,在一个应用程序中管理有关对话框和模式表单的所有内容非常复杂。
【解决方案3】:

这是一个类似的 StackOverflow 问题: Multiple app windows activation not working correctly

就我而言,我不会像 Craig 所描述的那样避免使用 MainForm。相反,我隐藏了主窗口,我所有的真实窗口都是其他非模态窗体。我对我的应用程序的工作方式很满意,但 Craig 的方法可能更简单。

查看我对上述问题的回答,查看我的方法的代码示例以及一些具有良好背景信息的链接。

【讨论】:

  • 谢谢马克,我认为它已经开始变得复杂了。我想我先尝试了最简单的方法,然后继续前进,直到达到我的目标,但很高兴看到很多好的答案。我需要我能得到的所有信息。
【解决方案4】:

在 Delphi 应用程序中创建的第一个窗体被视为主窗体,当该窗体关闭时应用程序终止。显而易见的解决方案是让第一个表单不是由用户关闭的表单,而是对用户不可见且仅在所有其他表单都关闭时才关闭的表单。

我没有尝试过,但它应该可以工作。

【讨论】:

    【解决方案5】:

    现在回答太晚了,但我遇到了同样的问题。我选择的解决方案是提取Application.ExeName 并将其传递给createProcess 甚至shellExecute 之类的函数。所以,现在我在操作系统级别有独立的应用程序。对于不同的实例,我还需要不同的任务栏按钮。

    【讨论】:

      猜你喜欢
      • 2011-04-02
      • 1970-01-01
      • 2011-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多