【问题标题】:How to Communicate between ViewModels?如何在 ViewModel 之间进行通信?
【发布时间】:2014-04-18 18:37:58
【问题描述】:

我正在构建我的第一个 WPF 和 MVVM 应用程序。我有 3 个视图(带有 3 个对应的 ViewModel)

1) 主窗口(窗口)

2) ViewClients (UserControl)

3) ViewModClient(用户控件)

在客户端 ViewModel 中,我有一个属性 SelectedClient,用于跟踪视图中 DataGrid 上选定的客户端。在这个视图中,我还有一个与 MainWindow ViewModel 中定义的 ICommand 相关的按钮。 我用这个绑定解决了它:

Command="{Binding Path=DataContext.CreateViewsCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" CommandParameter={x:Type local:ViewModClient}

我使用 CommandParameter 告诉 mainWindow ViewModel 要创建哪种视图,并使视图模型与视图知识隔离。这很好用。

现在的问题:

在 ModClient ViewModel 中,我需要加载客户端的数据。那么如何在保持 MVVM 模式的同时将其与客户端视图中的 SelectedClient 相关联? (我只能使用一个CommandParameter并且已经使用了)

【问题讨论】:

  • 也许使用某种形式的 EventAggregator?您可以从 ViewClients VM 中引发事件并在 ViewModClient VM 中订阅它。
  • 感谢您的回答。你能给我一些示例代码吗?也许您可以将其发布为建议答案。
  • 查看 MVVM Light Toolkit,它具有内置消息支持mvvmlight.codeplex.com
  • 也可以考虑卡特。它是一个功能齐全的 MVVM 框架,并带有在线模板和一个很好的入门指南 (catelproject.atlassian.net/wiki/display/CTL/…)。这意味着您可以尝试并体验它。它包含几种视图模型之间通信的方法:1)基于属性的通信 2)消息中介

标签: c# wpf mvvm viewmodel


【解决方案1】:

例如,Caliburn.Micro 有一个内置的IEventAggregator,它允许您创建也可以订阅的事件管理器。如果您使用多个 ViewModel 订阅此特定 EventAggregator,则可以定义可以从订阅此 EventAggregator 的所有 ViewModel 发布和接收的消息。

以下只是一个简短的实现,并展示了如果您使用像 Caliburn.Micro 这样的框架,ViewModel 之间的通信是多么容易:

class ViewModel1 : PropertyChangedBase
{
    private IEventAggregator _Event;
    public ViewModel1(IEventAggregator events)
    {
        _Event = events;
        _Events.Publish(new TestEvent(5));
    }
}

class ViewModel2 : PropertyChangedBase, IHandle<TestEvent>
{
    private IEventAggregator _Events;
    public ViewModel2(IEventAggregator events)
    {
        _Events = events;
        _Events.Subscribe(this);
    }

    public void Handle(TestEvent message)
    {
        // do something with the incoming message
    }
}

class TestEvent
{
    public int foo { get; set; }
    public TestEvent(int someint)
    {
        foo = someint;
    }
}

这应该是不言自明的。但是,如果您有任何问题,请告诉我,我会详细说明。

【讨论】:

    【解决方案2】:

    通常这是通过使用消息传递来完成的。

    所有 MVVM 框架都有一个消息总线来允许 VM 间通信,所以只要获得一个好的框架,任务就会变得如此简单 (Simple MVVM Toolkit):

    源虚拟机:

    SendMessage(MessageTokens.SomeToken, new NotificationEventArgs<string>
                (MessageTokens.SomeToken, "MyMessage"));
    

    接收虚拟机:

    RegisterToReceiveMessages<string>(MessageTokens.SomeToken, OnMessageReceived);
    
    private void OnMessageReceived(object sender, NotificationEventArgs<string> e)
    {
        // Code to execute upon message reception.
    }
    

    【讨论】:

    • 感谢您的回答,但目前我正在尝试远离外部框架。我不想在我的应用程序中添加另一个变量和问题来源。还有其他选择吗?
    • 如果您不想使用第三方 MVVM 框架,有几个选项。 1) 实现你自己的 Messenger 类。 2)使用事件,并让虚拟机订阅彼此的事件。那就是说; MVVM 不仅仅是一个实现 INotifyPropertyChanged 的​​类。它是一种由多个部分组成的设计模式,而 Messenger 就是其中的一部分。使用第三方框架比尝试使用自己的框架更容易。
    • 嗨@BTownTKD,感谢您的回答。我是所有这些技术的新手,我很难理解 mvvm 模式。现在,我设法解决了导航问题,并在没有外部框架的情况下加载了多个视图,事实上,我没有看到引入新的 sintaxis 和我并不真正需要的对象的意义。我尝试使用 Caliburn.Micro 但似乎“重新开始”来理解他的 sintaxis。如果我不使用框架,我还缺少什么?我将尝试使用事件并订阅视图模型。谢谢!
    • Caliburn 不适合 MVVM 新手,请使用 Simple MVVM Toolkit 或 MVVM Light 并记住,如果您不使用框架并且想要观察 MVVM 模式,您最终将拥有自己实现一个框架的所有核心功能,您认为哪个更好,使用许多开发人员使用的已建立的、经过同行评审的开源框架,还是在您自己的代码中重新创建所有这些功能?
    猜你喜欢
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    相关资源
    最近更新 更多