我需要一个应用程序架构建议。
我正在构建一个支持通知区域图标的.Net 4 WPF桌面应用程序。
应用程序有几个窗口,在启动时显示,然后关闭,只剩下通知区域图标。
通知区域图标纯粹是我从this代码项目示例中获得的WPF控件。
由于即使关闭了所有窗口,我的应用程序仍应继续运行,因此我设置了
ShutdownMode="OnExplicitShutdown"
在App.xaml。
我将描述我对架构和启动机制的想法,你告诉我我哪里出错了,并在可能的情况下纠正我。
在App.xaml.cs中,我创建了一个Ninject StandardKernel
,让我们称之为appKernel
并将Ninject模块加载到其中。起初只有一个接口由Ninject解决 - 一个heartbeatManager
实例。 HeartbeatManager是我计划用于的类:
a)将我的NotifyIcon实例作为字段变量托管,以便只要类实例在内存中就可以显示它。
b)关闭事件的实现,我将在app.xaml.cs中订阅并在heartbeat类请求时显式关闭应用程序。
此时创建一个heartbeatManager
实例,只是留在内存中。
在App.xaml中,我设置StartupUri="MainWindow.xaml"
,以便创建并显示MainWindow。遵循MVVM Light ViewModelLocator模式,MainWindow尝试从App.xaml中定义的静态ViewModelLocator实例解析它的数据上下文:
<Application.Resources>
<ViewModels:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>
创建ViewModelLocator实例,并在构造函数中初始化另一个StandardKernel(是否还有其他类型的内核,我此时会使用?),它将用于解析视图模型绑定。提供MainWindow的视图模型以满足窗口的请求,整个应用程序继续运行。
一个非常重要的含义是,hearbeatManger实例绑定到单一作用域中的接口定义,因此需要它作为构造函数参数的视图模型可以解析并获取已创建的实例。如果视图模型和heartbeatManager加载到不同的内核中,这样的依赖项解析是否会起作用?如果没有,我该如何解决这个问题?
以上计划的场景是否在架构方面是否良好,是否可以在代码中实现,或者在我思考架构时是否犯了错误?
答案 0 :(得分:3)
...此时创建一个heartbeatManager实例,只是留在内存中......
如果它是在你的内核中注册的,那么它就不会导致它在内存中挂起 - 它只要内核/容器就会存在。该容器可能应该是您的App类的成员,因为只要(编译器生成的)Main
方法,您的app类就会存在,并且无论如何都应该在app shutdown上处理它。
...遵循MVVM Light ViewModelLocator模式,MainWindow尝试从App.xaml中定义的静态ViewModelLocator实例解析它的数据上下文......
我不确定这会与NInject很好地融合。它是一个依赖注入容器,而不是服务定位器容器(即使它使用了一个容器)。 NInject(和类似框架)的主要目的是避免要求您使用服务定位器模式,而是注入所有依赖项。
...创建ViewModelLocator实例,并在构造函数中初始化另一个StandardKernel ...
除非您的场景非常复杂(实际上并非此应用程序),否则我建议您坚持使用单个NInject内核。
建议的解决方案
视图模型定位器类本身对您没有太大帮助,除了让您分割初始化逻辑,并根据您是处于设计模式还是运行时模式进行选择性初始化。您可以使用NInject模块(the view model locator article you linked in comments already uses)实现相同的目标。
我建议您不要使用视图模型定位器,而是在模块类中指定 all 组件,并在IsInDesignMode
方法中执行Load
逻辑。 / p>
这可能对MVVM来说有点棘手,因为视图模型需要绑定到您没有创建的object
属性,并且无法注释。
有几种方法可以直接在NInject中解决这个问题,而不是求助于服务定位器:
DataContext
。ToMethod
)创建视图,并将视图模型绑定到每个工厂方法的视图中。Bind<MainView>().ToMethod(context => new MainView() { DataContext = new MainViewModel() });
如果可以,我会选择第二种方法。这样您的视图就不必处理数据绑定,也不必有任何代码。它也很容易理解,可以避免在内核中注册视图和查看模型,并且可以避免为视图模型制作任何类型的知名接口。