MVVM - 用户控制相互通信的理想方式是什么

时间:2009-11-25 17:30:42

标签: c# wpf mvvm user-controls

我有一个用户控件,其中包含其他几个用户控件。我正在使用MVVM。每个用户控件都有一个相应的VM。这些用户控件如何相互发送信息?我想避免在后面的xaml代码中编写任何代码。特别是我对控件(主用户控件内部)如何相互通信以及如何与容器用户控件进行通信感兴趣。

修改: 我知道使用事件代表将帮助我解决这个问题。但是,我想避免在xaml代码隐藏中编写任何代码。

9 个答案:

答案 0 :(得分:14)

通常情况下,最好尽量减少部件之间的通信量,因为每次两个用户控件相互“对话”时,就会在它们之间引入依赖关系。

话虽如此,有几件事需要考虑:

  • UserControls可以通过公开属性和使用DataBinding始终与其包含的控件“对话”。这非常好,因为它在所有方面都保留了MVVM样式。
  • 包含控件可以使用属性将两个用户控件上的两个属性“链接”在一起,同样保留干净的边界

如果您确实需要更明确的沟通,有两种主要方法。

  1. 实现两个元素共有的服务,并使用依赖注入在运行时提供实现。这使得控件可以与服务进行通信,从而可以使控件保持同步,但也可以将依赖性保持在最低限度。
  2. 使用某种形式的消息传递在控件之间传递消息。许多MVVM框架采用这种方法,因为它将发送消息与接收消息分离,同样,将依赖性保持在最低限度。

答案 1 :(得分:8)

你的概念问题在这里:

  

每个用户控件都有一个相应的VM。

为每个视图设置一个单独的ViewModel几乎违背了ViewModel的概念。 ViewModels不应该与视图一对一,否则它们只不过是美化代码隐藏。

ViewModel捕获“当前用户界面状态”的概念 - 例如您所在的页面以及是否正在编辑 - 而不是“当前数据值”。

要真正获得M-V-VM的好处,请根据需要状态的不同项目确定所使用的ViewModel类的数量。例如,如果您有一个项目列表,每个项目都可以在3种状态下显示,则每个项目需要一个VM。相反,如果您有三个视图,所有视图都以3种不同的方式显示数据,具体取决于常见设置,则应在单个VM中捕获常用设置。

一旦您构建了ViewModel以反映手头任务的要求,您通常会发现不需要也不希望在视图之间传递状态。如果有此需求,最好的办法是重新评估您的ViewModel设计,看看共享的ViewModel是否可以从少量额外的状态信息中受益。

有时,应用程序的复杂性决定了对同一个模型对象使用多个ViewModel。在这种情况下,ViewModel可以保持对公共状态对象的引用。

答案 2 :(得分:3)

这有很多不同的机制,但您应首先了解此通信所属的架构层。

MVVM框架的一个目的是可以在同一个视图模型上创建不同的视图。这些用户控件是否仅在您当前正在实施的视图中相互通信,或者他们是否必须在其他可能的视图中相互交谈?在后一种情况下,您希望在视图级别下面的视图模型或模型本身中实现它。

第一种情况的示例可能是您的应用程序在非常小的显示器表面上运行。也许您的用户控件必须竞争视觉空间。如果用户单击一个用户控件以使其最大化,则其他用户控件必须最小化。这与viewmodel无关,它只是对技术的适应。

或者您可能有不同的用户控件的视图模型,可以在不更改模型的情况下进行操作。这方面的一个例子可能是导航。您有一个列表,以及一个详细信息窗格,其中包含与列表中所选项目相关联的字段和命令按钮。您可能希望单元测试为哪些项启用哪些按钮的逻辑。模型不关心您正在查看的项目,仅在按下按钮命令或更改字段时。

这种沟通的需求甚至可能在模型本身。也许您已经更新了非规范化数据,因为其他数据已更改。然后,由于模型中的变化波动,各种视图模型必须发生变化。

所以,总结一下:“这取决于......”

答案 3 :(得分:1)

我认为最好的解决方案是使用Publisher / Subscriber模式。每个控件都会注册一些事件,并将删除操作附加到其他控件公开的事件中。

为了公开事件并附加到它们,您需要使用某种Mediator / EventBroker服务。我找到了一个很好的例子here

答案 4 :(得分:1)

在我看来,最好的方法是通过命令(路由命令/中继命令等)。

  

我想避免在后面的xaml代码中编写任何代码。

虽然这是一个值得称赞的目标,但你必须对此应用一些实用性,它不应该100%作为“你不应该”的规则应用。

答案 5 :(得分:0)

您可以使用元素绑定在UI上的元素之间进行通信,因此假设您创建的用户控件公开了属性,则其他用户控件可以绑定到该属性。您可以配置绑定,使用依赖属性而不是基本属性/实现INotifyPropertyChanged但理论上可行,但确实需要一些预先考虑以这种方式进行通信。

您可能会发现使用事件,代码和属性的组合比尝试纯粹的声明方式容易得多,但理论上可行。

答案 6 :(得分:0)

您可以在控件和命令...

之间共享一些View Model对象

例如,您有一些主控件,其中包含另外两个控件。并且您在主控制中有一些过滤功能,但您希望允许用户在第一个子控件中设置过滤器的某些部分(如“完全过滤器”),在另一个子控件中设置过滤器的某些部分(如“快速过滤器”) “)。您还希望能够从任何子控件开始过滤。然后你可以使用这样的代码:

public class MainControlViewModel : ObservableObject
{
    public FirstControlViewModel firstControlViewModel;
    public SecondControlViewModel firstControlViewModel;

    public ICommand FilterCommand;
    public FilterSettings FilterSettings;

    public MainControlViewModel()
    {
        //...

        this.firstControlViewModel = new FirstControlViewModel(this.FilterSettings, this.FilterCommand);
        this.secondControlViewModel = new SecondControlViewModel(this.FilterSettings, this.FilterCommand);
    }
}

public class FirstControlViewModel : ObservableObject
{
    //...
}

public class SecondControlViewModel : ObservableObject
{
    //...
}

在主控件XAML中,您将子控件DataContext绑定到适当的View Models。每当子控件更改过滤器设置或执行命令时,将通知其他子控件。

答案 7 :(得分:0)

正如其他人所说,你有几个选择。

在用户控件上公开DepedencyProperties并绑定到这些属性在大多数情况下提供了纯XAML解决方案,但可以引入一些UI依赖关系以使绑定看到彼此

另一个选项是在ViewModel之间发送消息的解耦消息传递模式。我希望你的用户控件绑定到他们自己的VM上的属性,然后在该VM内部的属性更改它可以“发布”一条消息,通知其他“订阅者”发生了某些事情,他们可以对该消息作出反应,但是他们想要

如果有帮助,我会在这个主题上发布一篇博文:http://www.bradcunningham.net/2009/11/decoupled-viewmodel-messaging-part-1.html

答案 8 :(得分:0)

如果你使用的是严格的MVVM,那么用户控件就是一个View,只能与它的ViewModel“对话”,或者更确切地说, bind 。由于您的ViewModel很可能已经实现了INotifyPropertyChanged,只要它们彼此具有引用,它们就可以使用PropertyChanged事件在属性更改时得到通知,或者它们可以调用方法(如果通过接口更好),以便与每个方法进行通信其他