【问题标题】:Correct way to conduct multiple windows in Caliburn Micro?在 Caliburn Micro 中执行多个窗口的正确方法?
【发布时间】:2015-08-06 07:35:31
【问题描述】:

我正在使用 Caliburn.Micro 开发一个应用程序。我需要同时在多台显示器上打开一组相同的窗口,但我不知道如何干净利落地做到这一点。

我的想法是这样的:

  • ChildViewModel / ChildView 包含我需要在多个屏幕上显示的内容。
  • ParentViewModel 继承自 ConductorBase<ChildViewModel>
  • 在创建ParentViewModel 时,它会创建所需数量的ChildViewModel,并使用WindowManager.ShowWindow 来显示它们。
  • 我的 WindowManager 实现负责将这些视图放置在它们应该在的位置。

上述所有工作,除了一个例外:ParentView 显示在它自己的窗口中。我怎样才能避免这种情况发生?做我想做的事情的规范方式是什么?

我想要做的是从单个导体引导多个窗口,而不是导体本身显示窗口。

【问题讨论】:

  • 你要做什么来打开这个窗口?单击一个按钮,启动您的应用程序?
  • 单击通知区域上下文菜单中的菜单项。它不应该是相关的。
  • 在这次点击中,您正在提出命令,对吗?所以你可以在这个命令中“打开”这个窗口。我不知道 Caliburn 是如何处理这个问题的,但是这个命令将是我尝试的位置
  • 我打开窗户没问题。如果您阅读该问题,您会发现问题在于它需要 ParentView 同时可见。
  • 我看过但可能没抓住重点。为什么需要 ParentViewModel/ParentView?它有什么作用?

标签: c# wpf caliburn.micro


【解决方案1】:

这个架构怎么样?

                          SystemTrayVM
      (VM for your notification area, its View is the sys tray icon)
                                |
                                |
                      MultipleDesktopManager
      (not a VM, not a ConductorBase, no View, not visible)
                                |
            .-------------------|------------------.
            |                   |                  |
            |                   |                  |
    SingleDesktopVM      SingleDesktopVM     SingleDesktopVM
(its View holds child views. If necessary, it can be a ConductorBase)
                                |
                  .-------------|------------.------ - - - .
                  |             |            |
                  |             |            |
         SideBarChildVM    MainChildVM   FooterChildVM
  • SystemTrayVM 负责通过通知区域提供的所有交互(右键单击菜单操作等)。它实际上不会打开/关闭用户可见的窗口,而是将这些命令转发给 MultipleDesktopManager

  • MultipleDesktopManager 不可见且不是 VM。它的职责是例如需要时打开/关闭所有单个桌面父窗口。作为完全独立的窗口,应该不需要一个完整的导体。根据我的经验,为它提供一个 IWindowManager 就足够了,这样它就可以实际显示/关闭单个桌面父窗口。也许它也可以激活/停用它们,但不需要跟踪 当前 活动项目或类似的东西。

  • SingleDesktopVM 负责其内部显示的所有子视图/VM。因此,如果需要,这可能是一个导体……这实际上取决于父级中那些子视图的生命周期。例如。如果有一个命令栏和一个主要内容并且它们总是在那里,那么您可能可以使用包含其他 VM 的更简单的父 VM。

最近,我们遇到并解决了 WPF 应用程序启动不可见的问题,仅在系统托盘中找到,并且能够显示/隐藏 实际 主应用程序窗口。 在那种情况下,我们将隐藏/显示主应用程序窗口的任务卸载到由 SystemTrayVM 调用的帮助程序类(不是 VM)。这相当于此架构中的 MultipleDesktopManager

关于仅从系统托盘开始的 WPF 应用程序,我们将 PoC on GitHub 放在一起,展示如何将 this WPF NotifyIcon 控件与 Caliburn.Micro ViewModel-first 方法和通过 Autofac 的 DI 完全集成。

编辑:理由是CM 提供的现成的Conductor 实现似乎更适合包含其他子windowswindow。如果支架本身不可见,那么这些导体实现可能不是完美的选择。正如您在 cmets 中所说,这就像需要一个指挥者不是也是一个屏幕。 CM composition doc page 上有一个关于“准导体”的有趣内容。最后,MultipleDesktopManager 的想法基本上是一个管理许多窗口的简单导体,没有实现 IConductor 接口但仍然使用 WindowManager为了正确处理 SingleDesktopVMs 生命周期。

【讨论】:

  • 谢谢。我已经看过你在 Github 上的工作,并使用了一些。你的描述基本上正是我想要的。我现在运行良好,但有一个例外。每当我关闭从托盘打开的第一个窗口时,整个应用程序都会退出。在引导程序中,我使用资源中的NotificationIcon。我的MultipleDesktopManager 使用IWindowManager.ShowWindow(new SingleDesktopVM()) 启动一个窗口,并使用vm.TryClose() 关闭它们。
  • 别介意我的最后评论。我显然没有阅读链接的 GitHub 存储库中的所有示例。出于某种原因,我实施了解决方案 3,认为我不需要托盘图标的托管窗口。但现在我意识到隐藏窗口是让应用程序在打开时保持运行的原因。谢谢。
  • 嗯...你认为这个由 4 部分组成的 PoC 系列可能像现在解释的那样具有误导性吗?我们在开头放了一个tl;dr;,基本上是说“跳到#4”。你认为我们能以某种方式改进自述文件吗?顺便说一句,很高兴知道它有帮助。
  • 不。这只是我寻找特定内容的一个案例,并认为我找到了它是因为我没有阅读所有内容。使这一点更清楚的建议(但它涉及大量工作)是有一个 git 存储库,其中包含不同级别的分支/标签,说明您如何从 1 移动到 4。:)
  • 确实如此,我已经看到其他时候这样做了。暂时,我只是带着一个更明显的免责声明:)欢呼
【解决方案2】:

也许我又错过了什么,但您可以根据您提供的信息进行以下操作

第 1 步。

  • 转到处理 Menuclick 的类 (VM)
  • 添加一个私有值ParentConductor,这将与您的ParentViewModel 执行相同的操作(为每个子项保存一个引用,如果一个子项已关闭,则关闭所有子项)

第 2 步。

  • 修改您的 MenuclickClickCommand,以便每次单击它时都遵循发生的情况
  • 输入您的ParentConductor
  • 创建您的孩子并将参考添加到您的ParentConductor
  • 使用 Caliburn 为每个孩子创建窗口

现在,如果一个已关闭,您应该能够关闭所有 Child's。 无需创建ParentView,因为您的指挥将与您的班级(VM)一样长。

您还可以添加一个方法,以便售票员在关闭他的 Child's 后能够将他自己从班级中删除

【讨论】:

  • 您的答案通常列出了您将执行哪些操作来显示一组窗口。这里的问题是如何正确地遵循 Caliburn.Micro 的约定。代码是必需的。
  • @VegardLarsen 为什么你使用ConductorBase<T> 而不是Conductor<T> 有什么特别之处吗?
  • 没有。只是方便。我的实现不一定正确,例如我认为我可能真正需要的是一个不实现 IScreen 的独立导体。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-06
  • 1970-01-01
  • 2022-08-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多