我应该在容器中注册ViewModel吗?

6
我应该在容器中注册ViewModel并从容器中解析吗?
好处: 1. 当视图模型被激活时,我可以执行一些操作 2. 容器将为我注入依赖项 3. ???
缺点: 1. 视图模型的生命周期管理可能会有些棘手: - 如果我将ViewModel设置为单例,则无法实例化相同类型的多个控件 - 如果我使ViewModel短暂,则很容易出现实际期望注入相同实例而得到多个不同实例的情况。 2. ???
什么是正确的答案?如果我能够减轻生存期问题,我更倾向于注册。
我正在使用Caliburn和Autofac,如果这有关系。
3个回答

5
一个容器是由它所创建的对象所居住的生态系统。视图模型与这些居民进行交互,因此也是生态系统的一部分。为了准确反映这种关系,您应该在容器中注册视图模型。
您应始终使用InstancePerDependency来处理视图模型。视图模型表示特定UI部件的状态和行为 - 它是控件的非框架特定类比。就像通常不能在UI树中的两个位置放置同一个控件实例一样,您也不能重用相同的视图模型实例。
如果可以的话,我们将称其为ViewsModel :-)

1
那么当一个ViewModel需要注入到其他多个ViewModel中时怎么办呢?最简单的例子就是状态栏。假设我们在窗口上有几个控件(由几个ViewModel支持),每个控件不时地更新状态栏中的消息。 - Konstantin Spirin
关于使用ViewModel同时支持多个控件,考虑使用网格(Grid)。当您选择一行时,您希望看到详细信息。当前行和详细信息控件很可能绑定到同一个ViewModel上,只是以不同的方式显示它们。 - Konstantin Spirin
@Konstantin Spirin:对于状态栏,您可以使用共享服务来实现视图模型之间的通信,而不是直接共享视图模型(@Peter Lillevold也建议这样做)。对于网格/行示例,我几乎总是会为行创建一个视图模型,并为详细信息对话框创建一个单独的视图模型。它们可能都使用相同的实体实例,但将具有不同的配置文件和意图,例如在行上进行编辑命令,在对话框上进行保存/取消命令。 - Bryan Watts
@Konstantin Spirin:我理解你的例子。我的回应是,你不会将 StatusBarViewModel 注入到 MainWindowViewModel 中。相反,你会将类似 IStatusContext 的东西注入到 两个 StatusBarViewModelMainWindowViewModel 中。如果你使用 InstancePerLifetimeScope 注册 IStatusContext,你就可以在不损害视图模型/视图基数的情况下获得所需的结果。 - Bryan Watts
1
@Konstantin Spirin:我刚意识到你建议将StatusBarViewModel注入到StatusBarService中。而我则建议完全相反:IStatusContext应该是两个视图模型之间通信的点,但它本身不引用任何视图模型。因此,只需要解决一次StatusBarViewModel,就可以避免那些问题。 - Bryan Watts
显示剩余2条评论

4

2)的优点足以让我让容器处理viewmodels。我们使用自己的MVVM框架,在视图实例和viewmodel实例之间有严格的一对一关系,因此1)的缺点不存在。

在需要在视图之间共享数据的情况下,我们通过向viewmodels注入共享服务实例来实现。

除此之外,还有其他情况您希望一个viewmodel实例被多个视图共享吗?


假设我们有一个带有日期选择器和多个选项卡的窗口。每个选项卡都包含报告,所有报告都应使用日期选择器中相同的日期。您会如何实现这个要求? - Konstantin Spirin
1
@Konstantin - 我会将这样的视图视为多个视图(具有相应的视图模型)。一个视图/模型代表处理所选日期的整个窗口,然后每个选项卡都有一个视图/模型。使用数据绑定,您可以轻松地将主视图中的SelectedDate绑定到每个子视图上的属性。 - Peter Lillevold

0

关于autofac或Caliburn我不确定(可能仍然适用),但是当涉及到Unity容器时,我只会在以下情况下注册ViewModel...

当容器被处理时,我需要将其处置。您可以创建一个生命周期管理器,它将存储新创建的(非单例)视图模型实例。

container.RegisterType<MyViewModel>(new DisposeableInstanceLifetimeManager());
...
container.Resolve<MyViewModel>();  // here all dependencies will get injected
...
container.Dispose(); 

如果您想共享数据,我倾向于创建一个子容器并将模型注册为单例,让多个视图模型共享同一个模型。
var child = container.CreateChildContainer();
child.RegisterInstance(model, new ContainerControlledLifetimeManager());
child.Resolve<MyViewModel1>();
child.Resolve<MyViewModel2>(); // both can share the model instance

(请注意:使用 ViewModel 上的 Resolve 时,所有依赖项都将注入,即使未向容器注册。)
否则,除非您需要单例 ViewModel(尽管我无法想象何时会有用),否则我认为将 ViewModel 添加到容器中只会增加更多代码,没有任何好处。

应用程序的主窗口是单例的好选择。主窗口通常包含整个应用程序中共享的元素。例如,状态栏。使用Unity时,我做了非常类似的事情,但很容易迷失方向,因为您需要考虑要注册哪个ViewModel,哪个不需要注册。此外,将ViewModel作为依赖项也相当可怕,因为即使您没有注册它,Unity也可以创建新实例。 - Konstantin Spirin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接