我需要应用程序架构建议。
我正在构建一个支持通知区域图标的 .Net 4 WPF 桌面应用程序。
应用程序有几个窗口,在启动时显示,然后关闭,仅保留通知区域图标。
通知区域图标纯粹是我从this获得的WPF控件代码项目示例。
由于即使所有窗口都关闭,我的应用程序也应保持运行,因此我设置了
ShutdownMode="OnExplicitShutdown"
在 App.xaml 中。
我将描述我的架构和启动机制的想法,你告诉我哪里出错了,并在可能的情况下纠正我。
在 App.xaml.cs 中,我创建一个 Ninject StandardKernel
,我们将其命名为 appKernel
并将 Ninject 模块加载到其中。首先,Ninject 只解析一个接口(interface) - 一个 heartbeatManager
实例。 HeartbeatManager 是我计划用于以下用途的类:
a) 将我的 NotifyIcon 实例托管为字段变量,以便只要类实例在内存中,它就保持可见。
b) 关闭事件的实现,我将在 app.xaml.cs 中订阅该事件,并在心跳类请求时显式关闭应用程序。
此时,一个 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(此时我是否需要使用任何其他类型的内核?),该实例将用于解析 View 模型绑定(bind)。提供MainWindow的 View 模型来满足窗口的请求,并且整个应用程序继续运行。
一个非常重要的含义是,hearbeatManger 实例绑定(bind)到单调范围中的接口(interface)定义,以便需要它作为构造函数参数的 View 模型可以解析并获取已创建的实例。如果 View 模型和 heartbeatManager 加载到不同的内核中,这种依赖关系解析是否有效?如果没有,我该如何解决这个问题?
上述计划的场景在架构方面是否良好,是否可以在代码中实现,或者我在思考架构时是否犯了错误?
最佳答案
...At this point a heartbeatManager instance is created just left to hang in the memory...
如果它已在您的内核中注册,则不会有任何魔法导致它挂起在内存中 - 只要内核/容器存在,它就会保留在内存中。该容器可能应该是您的 App 类的成员,因为只要(编译器生成) Main
,您的应用程序类就会一直存在。方法确实如此,无论如何您都应该在应用程序关闭时将其处置。
...Following the MVVM Light ViewModelLocator pattern the MainWindow tries to resolve it's data context from the static ViewModelLocator instance defined in App.xaml...
我不确定这是否能与 NInject 很好地混合。它是一个依赖注入(inject)容器,而不是服务定位器容器(尽管它在底层使用了一个容器)。 NInject(和类似框架)的主要目的是避免要求您使用服务定位器模式,而是注入(inject)所有依赖项。
...ViewModelLocator instance is created and in the constructor it initializes another StandardKernel...
除非您的场景非常复杂(这确实不适合此应用程序),否则我建议坚持使用单个 NInject 内核。
建议的解决方案
View 模型定位器类本身并没有为您做太多事情,只是让您分割初始化逻辑,并根据您处于设计模式还是运行时模式进行选择性初始化。您可以使用 NInject 模块( the view model locator article you linked in comments already uses )实现相同的目的。
我建议您不要使用 View 模型定位器,而是在模块类中指定所有组件,然后执行 IsInDesignMode
Load
中的逻辑方法。
这对于 MVVM 来说可能有点棘手,因为 View 模型需要绑定(bind)到 object
属性不是您创建的,并且无法注释。
有几种方法可以直接在 NInject 中解决这个问题,而不是求助于服务定位器:
- 在 View 上使用依赖注入(inject),使其需要 View 模型。
如果使用构造函数注入(inject)不起作用(正如您在评论中提到的),请改用属性注入(inject),并使用将其 setter 转发到DataContext
的自定义属性。 . - 使用NInject factory methods (
ToMethod
) 创建 View ,并将 View 模型绑定(bind)到每个工厂方法中的 View 。
例如。Bind<MainView>().ToMethod(context => new MainView() { DataContext = new MainViewModel() });
如果可以的话,我会选择第二种方法。这样您的 View 就不必处理数据绑定(bind),也不必有任何后面的代码。它也很容易理解,可以让您避免在内核中注册 View 和 View 模型,并且可以让您避免为 View 模型创建任何类型的众所周知的接口(interface)。
关于architecture - MVVM Light,Ninject 在大多数通知区域应用程序中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7781170/