c# - 当所有 View 都被停用时,如何阻止 Caliburn Micro 关闭应用程序?

标签 c# .net wpf winforms caliburn.micro

我正在将一个旧的 winforms 程序升级到 WPF,该程序将 Caliburn Micro 用于 MVVM,将 Ninject 用于 DI。

我已将我的解决方案更新为运行 Caliburn Micro Bootstrap 的非通用实现的 WPF 解决方案。我这样做是因为我目前没有可运行的基本 ShellViewModel。

我使用的是 Ninject 风格的 Bootstrap ,而不是标准的 Caliburn Micro Bootstrap ,因为我对 Ninject 相当熟悉。

Bootstrap 看起来像这样:

public class NinjectBootstrapper : Bootstrapper
{
    private const string EXTENSION_PATH = "Plugins";
    public static IKernel Kernel;

    protected override void Configure()
    {
        Kernel = new StandardKernel();

        Kernel.Bind<IEventAggregator>()
              .To<EventAggregator>()
              .InSingletonScope();

        Kernel.Bind<ViewModels.IAnalysisOutputTableViewModel>()
              .To<ViewModels.AnalysisOutputTableViewModel>()
              .InSingletonScope();

        Kernel.Bind<IWindowManager>()
            .To<WindowManager>()
            .InSingletonScope();


        Kernel.Bind<FormMain>()
              .ToSelf();
    }

    protected override object GetInstance(Type serviceType, string key)
    {
        if (serviceType != null)
        {
            return Kernel.Get(serviceType);
        }

        throw new ArgumentNullException("serviceType");
    }

    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        return Kernel.GetAll(service);
    }

    protected override void BuildUp(object instance)
    {
        Kernel.Inject(instance);
    }
}

为了让我的主窗体启动,我被迫在我的 App.xaml.cs 中重写 OnStartup:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        NinjectBootstrapper.Kernel.Get<FormMain>().Show();
    }
}

如上所示,我将我的主要 Ninject 内核作为公共(public)静态公开以访问它,但我意识到这可能是一种反模式,一旦我完成程序转换,我将解决这个问题。

我已经使用适当的 MVVM 为我的主窗体实现了一个新的子窗口。我需要从这样的按钮打开窗口:

private void dataViewerButton_Click(object sender, EventArgs e)
{
    windowManager.ShowWindow(analysisOutputTableViewModel);
}

到目前为止一切顺利。

问题是当我试图关闭这个子窗口时。它不仅关闭了窗口,还关闭了我的整个程序。

我想这是因为,因为这个子窗口是 Caliburn 正在处理的唯一窗口(或 View ),一旦它被停用,Caliburn 会为我决定我要关闭程序。

有什么办法可以阻止这种自动关闭行为吗?也许我应该以不同的方式初始化我的主窗体?


更新

使用下面 Marwijn 的回答,我能够正确关闭我的应用程序。

最后,我在主窗体的关闭事件中使用了 Application.Exit();:

private void Form_Main_FormClosing(object sender, FormClosingEventArgs e)
{
    Application.Exit();
}

结合对我的 App.xaml.cs 的以下修改:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var mainForm = NinjectBootstrapper.Kernel.Get<FormMain>();
        mainForm.Show();

        mainForm.FormClosed += mainForm_FormClosed;
    }

    void mainForm_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e)
    {
        this.Shutdown(0);
    }
}

我现在可以使用 Caliburn Micro 和 Ninject 从我的主窗体加载 WPF 窗口,并且一切正常关闭并且不会让任何进程运行。在我能够将主窗体更新为 WPF 之前,此解决方案似乎会运行良好。

最佳答案

原因看起来是 WindowManager在 WPF 中是什么在跟踪你的根 VM/窗口,因为你的启动代码绕过了 WindowManager并调用 Show直接在窗口上,CM 无法知道跟踪它。

检查 CM 代码并查看 Bootstrapper<T>实现在它调用的启动时显示:

protected void DisplayRootViewFor(Type viewModelType, IDictionary<string, object> settings = null) 
{
    var windowManager = IoC.Get<IWindowManager>();
    windowManager.ShowWindow(IoC.GetInstance(viewModelType, null), null, settings);
}

所以看起来 CM 已经解析了根 VM 的实例并通过窗口管理器显示它。

有什么理由不能使 Bootstrap 通用并使用 FormMain作为通用类型(或者这只是一个 View ?在这种情况下为它创建一个 View 模型那么复杂??)

public class NinjectBootstrapper : Bootstrapper<FormMain>

如果不是

public class NinjectBootstrapper : Bootstrapper<FormMainViewModel>

这段代码:

NinjectBootstrapper.Kernel.Get<FormMain>().Show();

相当于

IoC.GetInstance(viewModelType, ... etc)

IoC.GetInstance只需调用 GetInstance在 Bootstrap 上,您的引导覆盖从容器中解析。

编辑:这里的奖励点是因为你从应用程序类中删除了对 ninject 内核的依赖,你可以使它成为非静态的并摆脱那个讨厌的静态容器解析

或者,您可以确保 WindowManager用于在您当前的代码中打开窗口:

var frmMain = NinjectBootstrapper.Kernel.Get<FormMain>();
windowManager.ShowWindow(frmMain);

(不确定这里的依赖关系,因为 Application 需要依赖 IWindowManager )

但仍然 - 我肯定会考虑不采用您目前采用的方法,而且我正在挠头想知道使用通用 Bootstrap 是否有任何问题?您不需要它的外壳,只需一个 CM 将用来解析 View 的 Root View 模型。 (如果使用 MainForm 不起作用,创建一个 View 模型来处理它应该是微不足道的......只需创建 MainFormViewModel 就可以了!你不需要连接它,它仍然可以是代码隐藏的,但可以由 CM 实例化使用 ViewModel-First 方法 - 在这种情况下它应该仍然有效)

编辑:我应该提一下,默认约定不会解析您的 FormMain查看,除非您将其重构为 FormMainView .或者改变默认约定

关于c# - 当所有 View 都被停用时,如何阻止 Caliburn Micro 关闭应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15944262/

相关文章:

c# - ListBox 复选框命令绑定(bind)

c# - 可以将 View 传递给 MVVM 中的 ViewModel 吗?

c# - 如何读取带有文件夹的事件日志?

c# - 上课吗?添加范围 ICollection?

c# - .NET 的工作流引擎

.net - VS Lightswitch 和 MS Access DB?

c# - 获取 UIElement 的高度和宽度?

c# - SqlDateTime 溢出,但我想要 NULL 或 MinValue

c# - 自定义通用集合 - 重构它的最佳方法是什么?

c# - 在没有云服务的情况下关闭应用程序时通知用户