我正在将旧的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之前,此解决方案看起来效果很好。
答案 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时,应用程序才会停止。