【问题标题】:MVVM: Loosely coupling of ViewModelsMVVM:ViewModel 的松散耦合
【发布时间】:2017-06-05 04:07:30
【问题描述】:

我有班级,A、B、C、D

A 引用了 E 和 B 以及 C 和 D 的列表。

在 AViewModel 中,这些被暴露在树中。

A 的视图如图所示。

当一个节点被选中时,应该显示一个对应的视图。

有 E、B、C 和 D 的 ViewModel

我的问题如下:

例如 E 是选定的节点。我将它作为“对象 SelectedItem”存储在 AViewModel 中。 以松散耦合的方式创建 EViewModel 的最佳方法是什么,这样我就不想在 AViewModel 中引用 EViewModel。

更新:

我正在考虑一种解决方案,但在其他地方我从未见过:

例如,我只能从 AViewModel 公开我的 POCO(B、C、D、E)。在 XAML 中,我可以将 ContentControl 直接绑定到这些对象。使用转换器,当我绑定到 B 等时,我可以拥有一个 BViewModel。

【问题讨论】:

  • 能否请您发布您的 xaml,这样我们就不必重新发明轮子了?

标签: wpf mvvm loose-coupling


【解决方案1】:

在 MVVM 应用程序中以松散耦合方式在视图模型和其他组件之间进行通信的一种常见且被广泛接受的方式是使用事件聚合器或信使。请参阅以下链接了解更多信息。

使用事件聚合器模式在视图模型之间进行通信: https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/

MVVM - MVVM 中的 Messenger 和查看服务: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx

另一种选择是使用您注入视图模型的共享服务:https://social.msdn.microsoft.com/Forums/en-US/22907a0f-d805-4195-8272-7c284b72d2ee/example-of-using-shared-services-prism?forum=wpf

使用事件聚合器、信使或共享服务意味着您可以删除视图模型类之间的所有引用。

与视图模型类之间来回相互强引用不同,每个视图模型只知道一个事件聚合器/信使/共享服务,并且只与该服务进行通信。视图模型 A 可以发送任意数量的消息,任何其他视图模型可以订阅以在内部收听和处理。

我的问题是关于在没有紧耦合的情况下创建 ViewModel 实例的最佳实践,而不是关于松耦合 ViewModel 之间的通信

创建另一个视图模型的实例而不创建字符串耦合是不可能的。如果一个视图模型创建另一个视图模型的实例,则它们根据定义是强耦合的。为了防止这种情况,您可以注入具有其他视图模型实现的接口类型的视图模型,例如:

public ViewModelB(IViewModelA viewModelA)
{
    //...
}

那么 ViewModelB 依赖于接口类型而不是 ViewModelA 的具体实现。这比做这样的事情要好一点,因为这样 ViewModelA 和 ViewModelB 将始终 - 如上所述 - 彼此强耦合:

public ViewModelB()
{
    _viewModelA = new ViewModellA();
}

但如果您真的关心视图模型类之间的松散耦合,您应该摆脱直接引用并开始使用事件聚合器或信使在它们之间进行通信。

【讨论】:

  • 我的问题是关于在没有紧密耦合的情况下创建 ViewModel 实例的最佳实践,而不是关于松散耦合的 ViewModel 之间的通信。
  • 这是不可能的。如果一个视图模型创建另一个视图模型的实例,则它们根据定义是强耦合的。有关更多信息,请参阅我编辑的答案。
  • @jannagy02 使用工厂方法并返回一个(实例)接口。或者使用 Prism 之类的 DI 容器 - 您请求一个类型(使用接口)并返回一个具体类型。但是对于您指定的内容,听起来 B、C、D 和 E 都是 POCO(可能用自己的视图模型包装) - AViewModel 可以了解它们,因为它的工作是塑造、转换和呈现数据视图。
  • @mm8 这并非不可能,例如 slugster 提到的工厂方法。我在帖子中添加了更新。
  • 如果工厂方法返回视图模型 B 的实例,并且您在视图模型 A 中调用工厂方法,则视图模型 A 与视图模型 B 强耦合。请再次阅读我的答案。
【解决方案2】:

如果您的 A 视图显示“列表”以及选定的视图,则在 AViewModel 中有 EViewModel 引用是完全可以接受的。 ViewModel 可能是视图的“反映”。因此,如果 A 视图将包含 EView,则 AViewModel 可能包含 EViewModel。您只需嵌套视图模型,以便它创建与视图层相同的树。

最重要的是,我要做的不是在 AViewModel 中引用 E 或 B,...,而是仅引用 EViewModel,BViewModel,... 所以 AView 列表不显示模型类,而是显示 ViewModel 类。您的 SelectedItem 被键入为 ViewModel,您可以直接将“显示”视图部分绑定到 SelectedItem。然后您可以在视图层上使用正确的 DataTemplate 来显示相应的视图。希望对你有帮助

【讨论】:

  • 这就是我目前正在做的事情:) 但我一直觉得这可以用更好的方式来完成。
猜你喜欢
  • 2020-10-26
  • 2011-02-22
  • 2010-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
  • 2013-10-11
  • 1970-01-01
相关资源
最近更新 更多