MVVM Light、Ninject 主要用于通知区域应用程序

发布于 2024-12-10 13:13:37 字数 1481 浏览 0 评论 0原文

我需要应用程序架构建议。

我正在构建一个具有通知区域图标支持的 .Net 4 WPF 桌面应用程序。

应用程序有几个窗口,在启动时显示,然后关闭,仅保留通知区域图标。

通知区域图标纯粹是我从 这个 codeproject 示例中获得的 WPF 控件。

由于即使所有窗口都关闭,我的应用程序也应保持运行,因此我

ShutdownMode="OnExplicitShutdown"

在 App.xaml 中设置了 a。

我将描述我的架构和启动机制的想法,你告诉我哪里出错了,并在可能的情况下纠正我。

在 App.xaml.cs 中,我创建一个 Ninject StandardKernel,我们将其命名为 appKernel 并将 Ninject 模块加载到其中。首先,Ninject 只解析一个接口 - 一个 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(还有其他类型的内核吗?此时使用?)将用于解析视图模型绑定。提供MainWindow 的视图模型来满足窗口的请求,并且整个应用程序继续运行。

一个非常重要的含义是,hearbeatManger 实例绑定到单调范围中的接口定义,以便需要它作为构造函数参数的视图模型可以解析并获取已创建的实例。如果视图模型和 heartbeatManager 加载到不同的内核中,这种依赖关系解析是否有效?如果没有,我该如何解决这个问题?

上述计划的场景在架构方面是否良好,是否可以在代码中实现,或者我在思考架构时是否犯了错误?

I need an application architecture advise.

I am building a .Net 4 WPF desktop application with notification area icon support.

Application has a few windows, that show up on startup, then get closed, and only the Notification Area icon remains.

The notification area icon is purely WPF control that I got from this codeproject example.

Since my app should remain running even when all the windows are closed, I have set a

ShutdownMode="OnExplicitShutdown"

in App.xaml.

I will describe my idea of an architecture and startup mechanism, and you tell me where I am going wrong and correct me if possible.

In App.xaml.cs I create a Ninject StandardKernel, let's call it appKernel and load Ninject modules into it. At first only one interface is to be resolved by Ninject - a heartbeatManager instance. A HeartbeatManager is a class I am planing to use for:

a) Hosting my NotifyIcon instance as a field variable, so that it reamins visible as long as the class instance is in memory.

b) Implementation of shutdown event, that I will subscribe for in app.xaml.cs and shutdown the application explicitly, when the heartbeat class requests it.

At this point a heartbeatManager instance is created just left to hang in the memory.

In App.xaml I set the StartupUri="MainWindow.xaml" so the MainWindow is created and shown. Following the MVVM Light ViewModelLocator pattern the MainWindow tries to resolve it's data context from the static ViewModelLocator instance defined in App.xaml:

<Application.Resources>
    <ViewModels:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>

ViewModelLocator instance is created and in the constructor it initializes another StandardKernel (is there any other type of kernel I shout use at this point?) that will be used to resolve the view model bindings. A MainWindow's view model is provided to satisfy the windows's request and the whole application continues to operate.

A very important implication, is that the hearbeatManger instance is bound to an interface definition in a singletone scope, so that view models that require it as a constructor parameter can resolve and get the already created instance. Will such dependency resolution work, if the view model and the heartbeatManager are loaded in a different kernels? If not, how can I solve this problem?

Is the above planned scenario any good architecture-wise, is it implementable in code, or have I made a mistake when I was thinking through the architecture?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

悲歌长辞 2024-12-17 13:13:37

...此时,将创建一个 heartbeatManager 实例并挂在内存中...

如果它已在您的内核中注册,则不会有任何魔法导致它挂在内存中 - 只要存在,它就会一直存在内核/容器可以。该容器可能应该是您的 App 类的成员,因为只要(编译器生成的)Main 方法存在,您的应用程序类就会一直存在,并且无论如何您都应该在应用程序关闭时将其处置。

...遵循 MVVM Light ViewModelLocator 模式,MainWindow 尝试从 App.xaml 中定义的静态 ViewModelLocator 实例解析其数据上下文...

我不确定这是否能与 NInject 很好地混合。它是一个依赖注入容器,而不是服务定位器容器(尽管它在底层使用了一个容器)。 NInject(和类似框架)的主要目的是避免要求您使用服务定位器模式,而是注入所有依赖项。

...ViewModelLocator 实例已创建,并在构造函数中初始化另一个 StandardKernel...

除非您的场景非常复杂(这确实不适合这个应用程序),那么我建议坚持使用单个 NInject 内核。

建议的解决方案

视图模型定位器类本身并没有为您做太多事情,只是让您分割初始化逻辑,并根据您处于设计模式还是运行时模式进行选择性初始化。您可以使用 NInject 模块(其中 您在评论中链接的视图模型定位器文章已使用)。

我建议您不要使用视图模型定位器,而是在模块类中指定所有组件,并在Load中执行IsInDesignMode逻辑方法。

不过,这对于 MVVM 来说可能有点棘手,因为视图模型需要绑定到您未创建的 object 属性,并且无法注释。

有几种方法可以直接在 NInject 中解决这个问题,而不是求助于服务定位器:

  • 在视图上使用依赖注入,使其需要视图模型。
    如果使用构造函数注入不起作用(正如您在评论中提到的),请改用属性注入,并使用将其设置器转发到 DataContext 的自定义属性。
  • 使用 NInject 工厂方法 ( ToMethod)来创建视图,并将视图模型绑定到每个工厂方法中的视图。
    例如 Bind().ToMethod(context => new MainView() { DataContext = new MainViewModel() });

如果可以,我会选择第二种方法。这样您的视图就不必处理数据绑定,也不必有任何后面的代码。它也很容易理解,可以让您避免在内核中注册视图和视图模型,并且可以让您避免为视图模型创建任何类型的众所周知的接口。

...At this point a heartbeatManager instance is created just left to hang in the memory...

If it is registered with your kernel, it isn't any magic that causes it to hang in memory - it will stick around as long as the kernel/container does. That container should probably be a member of your App class, since your app class sticks around as long as the (compiler generated) Main method does, and you should dispose it on app shutdown anyhow.

...Following the MVVM Light ViewModelLocator pattern the MainWindow tries to resolve it's data context from the static ViewModelLocator instance defined in App.xaml...

I'm not sure this will mix well with NInject. It is a Dependency Injection container, not a Service Locator container (even though it uses one under the hood). The main purpose of NInject (and similar frameworks) is to avoid requiring you to use the Service Locator pattern, and instead to inject all your dependencies.

...ViewModelLocator instance is created and in the constructor it initializes another StandardKernel...

Unless your scenario is quite complicated (which it really isn't for this app), then I suggest sticking to a single NInject kernel.

Suggested Solution

The view model locator class itself doesn't do much for you other than let you split up your initialization logic, and do selective initialization depending on whether you're in design mode or runtime mode. You could achieve the same with NInject modules (which the view model locator article you linked in comments already uses).

I suggest that instead of using a view model locator, you specify all your components in your module class, and do the IsInDesignMode logic in the Load method.

This might be a bit tricky with MVVM though, since the view model needs to bind to an object property you didn't create, and can't annotate.

There are a few ways to solve this directly in NInject, instead of resorting to a service locator:

  • Use Dependency Injection on your view, making it require a view model.
    If this doesn't work using constructor injection (as you mentioned in your comments), use property injection instead, with a custom property that forwards its setter to DataContext.
  • Use NInject factory methods (ToMethod) to create your view, and bind the view model to the view in each factory method.
    E.g. Bind<MainView>().ToMethod(context => new MainView() { DataContext = new MainViewModel() });

If you can, I'd go for the second method. This way your view doesn't have to handle data binding, and doesn't have to have any code behind. It is also simple to understand, would let you avoid having to register both views and view models in the kernel, and would let you avoid making any sort of well known interface for your view model.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文