如何在停用所有视图时停止Caliburn Micro关闭应用程序?

时间:2013-04-11 08:40:10

标签: c# .net wpf winforms caliburn.micro

我正在将旧的winforms程序升级到WPF,利用Caliburn Micro for MVVM和Ninject for DI。

我已将我的解决方案更新为WPF解决方案,该解决方案运行Caliburn Micro引导程序的非泛型实现。我这样做是因为我目前没有可以运行的基本ShellViewModel。

我使用的是Ninject风格的bootstrapper而不是标准的Caliburn Micro引导程序,因为我对Ninject非常熟悉。

引导程序如下所示:

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内核暴露为公共静态来访问它,虽然我意识到这可能是一个反模式,一旦我完成转换程序,我将解决这个问题。 / p>

我使用适当的MVVM实现了我的主窗体的新子窗口。我需要从按钮打开窗口:

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

到目前为止一切顺利。

问题是当我尝试关闭这个子窗口时。它不是关闭窗口,而是关闭我的整个程序。

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

有没有办法阻止这种自动关闭行为?也许我应该以不同的方式初始化我的主表单?


更新

使用Marwijn的答案,我能够正确地关闭我的应用程序。

最后,我在主要表单的Closing事件中使用了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之前,此解决方案看起来效果很好。

2 个答案:

答案 0 :(得分:3)

原因似乎是WPF中的WindowManager正在跟踪您的根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的一个实例并通过窗口管理器显示它。

有没有理由你不能让你的引导程序通用并使用FormMain作为泛型类型(或者这只是一个视图?在哪种情况下为它创建一个viewmodel那么复杂?)< / p>

public class NinjectBootstrapper : Bootstrapper<FormMain>

或者如果不是

public class NinjectBootstrapper : Bootstrapper<FormMainViewModel>

此代码:

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

相当于

IoC.GetInstance(viewModelType, ... etc)

因为IoC.GetInstance只是在引导程序上调用GetInstance而你的引导覆盖从容器中解析出来。

编辑:此处的奖励点因为您从应用程序类中删除了对ninject内核的依赖性,您可以将其设置为非静态并摆脱那个令人讨厌的静态容器解析

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

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

(由于Application需要依赖IWindowManager来确定依赖关系,所以

但是仍然 - 我肯定会考虑不采用您目前获得的方法,而且我对使用通用引导程序是否存在任何问题感到头疼?你没有需要这样的shell,只是一个CM将用来解析视图的根视图模型。 (如果使用MainForm不起作用,创建一个viewmodel来处理它应该是微不足道的...只需创建MainFormViewModel即可!它不需要它连线,它仍然可以是代码隐藏但可以由CM实例化使用ViewModel-First方法 - 在这种情况下它应该仍然有效)

编辑:我应该提一下,默认约定不会解析您的FormMain视图,除非您将其重构为FormMainView。要么是这样,要么改变默认约定

答案 1 :(得分:2)

您可以使用:

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml"
    ShutdownMode="OnExplicitShutdown"
   >
</Application>

这样,只有在显式调用shutdown时,应用程序才会停止。