1

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

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

我使用的是 Ninject 风格的引导程序,而不是标准的 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 内核公开为一个公共静态来访问它,尽管我意识到这可能是一种反模式,一旦我完成了程序的转换,我将解决这个问题。

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

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

到目前为止,一切都很好。

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

我想这是因为,由于这个子窗口是 Caliburn 正在处理的唯一窗口(或视图),一旦它被停用,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 之前,该解决方案似乎可以正常工作。

4

2 回答 2

3

原因似乎是WindowManagerWPF 中的内容正在跟踪您的根虚拟机/窗口,并且由于您的启动代码绕过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用作通用类型(或者这只是一个视图?在这种情况下为它创建一个视图模型那么复杂??)

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

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

编辑:我可能应该提到默认约定不会解析您的FormMain视图,除非您将其重构为FormMainView. 要么改变默认约定

于 2013-04-11T09:57:30.613 回答
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 时,应用程序才会停止。

于 2013-04-12T07:34:07.993 回答