wpf 我应该如何在 ViewModel 之间进行通信?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19029801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How should I communicate between ViewModels?
提问by Sam
I am using MVVM Light and have used the packaged messenger system to communicate between view models, however I have hit a bit of a dilemma! Basically when a user clicks on a customer record the corresponding view is opened and with it the CustomerViewModelis instantiated. At this point the CustomerViewModelrequires the selected customers ID from the previous view model (ViewAllCustomersViewModel) so that it can get selected customers info which the view binds to (still following?). So initially my thought was too send that ID in a message from the ViewAllCustomersViewModel(where the customer to be viewed is selected) to the CustomerViewModel... HOWEVER, the CustomerViewModelis not instantiated to be able to receive the message until the view is loaded (at which point the message has already been broadcast)!
我正在使用 MVVM Light 并使用打包的信使系统在视图模型之间进行通信,但是我遇到了一些困境!基本上,当用户单击客户记录时,会打开相应的视图,并使用它实例化CustomerViewModel。此时CustomerViewModel需要从先前的视图模型 ( ViewAllCustomersViewModel) 中选择客户 ID,以便它可以获取视图绑定到的选定客户信息(仍在关注?)。因此,最初我的想法是将该 ID 在从ViewAllCustomersViewModel(选择要查看的客户的位置)的消息中发送到CustomerViewModel......但是,CustomerViewModel直到视图加载(此时消息已经广播)才被实例化为能够接收消息!
So, what would be the best way to solve this issue? So far I have considered the CustomerViewModelsending a request to the ViewAllCustomersViewModelonce it has been instantiated (basically saying "I am ready to receive the message"), and then the ViewAllCustomersViewModelsending the ID back to the CustomerViewModel... but is this a necessary approach to solve this? It seems a bit ugly to me!
那么,解决这个问题的最佳方法是什么?到目前为止,我已经考虑过CustomerViewModel在实例化后向ViewAllCustomersViewModel发送请求(基本上是说“我准备好接收消息”),然后ViewAllCustomersViewModel将 ID 发送回CustomerViewModel...但这是一个解决这个问题的必要方法?在我看来有点丑!
Otherwise, I was thinking is there another way to communicate which can account for the issue I am having? But then isn't this the whole point of the messaging system... to be able to communicate between view models? OR can I force the view model to be instantiated on start up? If so, how would that affect the ViewModelLocator?
否则,我在想是否有另一种沟通方式可以解决我遇到的问题?但这不是消息传递系统的全部意义......能够在视图模型之间进行通信吗?或者我可以强制在启动时实例化视图模型吗?如果是这样,这将如何影响ViewModelLocator?
I hope I have outlined the issue clearly, I have used fictional view model names for the purpose of the explanation... and please feel free to edit or suggest any additional information that you would like me to add!
我希望我已经清楚地概述了这个问题,为了解释的目的,我使用了虚构的视图模型名称……请随时编辑或建议您希望我添加的任何其他信息!
采纳答案by Daniel
Did you try to communicate via your model? I was not able to read your topic until the end but this is how I communicate between ViewModels. Both View Models have the instance of session.
您是否尝试通过模型进行交流?直到最后我才能够阅读您的主题,但这就是我在 ViewModel 之间进行交流的方式。两个视图模型都有会话实例。
public ViewModel1(ISession session)
{
_session = session;
}
public ViewModel2(ISession session)
{
_session = session;
}
This way, when you test your application in BDD (behavior driven development), you can test your application without the view. The glue is the model.
这样,当您在 BDD(行为驱动开发)中测试您的应用程序时,您可以在没有视图的情况下测试您的应用程序。胶水是模型。
As you can see on this picture, you should be able to test your application without the view.

正如您在这张图片中看到的,您应该能够在没有视图的情况下测试您的应用程序。

回答by Boobalan
I came across the same situation where two view model is communicating each other. I have used Microsoft PRISM framework to publish and Subscribe.
我遇到了两个视图模型相互通信的相同情况。我使用 Microsoft PRISM 框架来发布和订阅。
In your case CustomerViewModel is parent View and ViewAllCustomersViewModel is child view.
在您的情况下 CustomerViewModel 是父视图, ViewAllCustomersViewModel 是子视图。
Download prism framework "Microsoft.Practices.Prism.PubSubEvents.dll" from https://www.nuget.org/packages/Prism.PubSubEvents/
Add prism reference to your project "Microsoft.Practices.Prism.PubSubEvents.dll"
Create some custom class which is used for communication modem.
class Notifications : PubSubEvent<string> { }Create
IEventAggregator eventAggregatorsingleton instance for your project and initialize it.public sealed class SessionInfo { public IEventAggregator eventHanlder; private SessionInfo (){ } private static SessionInfo _instance = null; public static SessionInfo Instance{ get{ lock (lockObj){ if (_instance == null) { _instance = new SessionInfo (); _instance.eventHanlder= new EventAggregator(); } } return _instance; } } }Go to Popover model (ViewAllCustomersViwModel) button events handling and below codes in it.Now it has been published.
从https://www.nuget.org/packages/Prism.PubSubEvents/下载棱镜框架“Microsoft.Practices.Prism.PubSubEvents.dll”
将棱镜引用添加到您的项目“Microsoft.Practices.Prism.PubSubEvents.dll”
创建一些用于通信调制解调器的自定义类。
class Notifications : PubSubEvent<string> { }IEventAggregator eventAggregator为您的项目创建 单例实例并初始化它。public sealed class SessionInfo { public IEventAggregator eventHanlder; private SessionInfo (){ } private static SessionInfo _instance = null; public static SessionInfo Instance{ get{ lock (lockObj){ if (_instance == null) { _instance = new SessionInfo (); _instance.eventHanlder= new EventAggregator(); } } return _instance; } } }转到 Popover 模型(ViewAllCustomersViwModel)按钮事件处理和下面的代码。现在它已经发布了。
In ViewAllCustomersViwModel.cs:
在ViewAllCustomersViwModel.cs:
public void OnSelectedItem(Item item)
{
SessionInfo.Instance.eventHanlder.GetEvent<Notification>().Publish(item.id);
}
- These event aggregator has to be subscribe where it is needed. So add below code on your Parent View model (CustomerViewModel)
- 这些事件聚合器必须在需要的地方订阅。因此,在您的父视图模型(CustomerViewModel)上添加以下代码
CustomerViewModel.cs
客户视图模型.cs
public class CustomerViewModel
{
public CustomerViewModel()
{
SessionInfo.Instance.eventHanlder.GetEvent<Notifications>().Subscribe(OnReceivedNotification);
}
//Handling the notification
public void OnReceivedNotification(string itemId)
{
Debug.WriteLine("Item Id is :" + itemId);
}
}
For more information:
想要查询更多的信息:
回答by Daryl Behrens
Personally, I used to use the MVVM-Light Messenger, but found I had way to many messages flying around, and I didn't like the feeling of using a "magical" messenger. What I did is outlined as the answer to the following link
就我个人而言,我曾经使用过 MVVM-Light Messenger,但发现我有很多消息飞来飞去,而且我不喜欢使用“神奇”的 Messenger 的感觉。我所做的被概述为以下链接的答案
Best Way to Pass Data to new ViewModel when it is initiated.
Now I warn you, I answered my own question, and nobody verfied it as good or bad practice, however it works for my situation and has elimnated the need for MVVM-Light Messenger. Because my program uses multiple threads in my implementation I changed all the entries in the repository to Dictionarys with the CurrentThread.ManagedThreadId as the Key.
现在我警告你,我回答了我自己的问题,没有人验证它是好还是坏的做法,但是它适用于我的情况并且已经消除了对 MVVM-Light Messenger 的需要。因为我的程序在我的实现中使用了多个线程,所以我将存储库中的所有条目更改为以 CurrentThread.ManagedThreadId 作为键的 Dictionarys。
回答by Pavel Tupitsyn
I believe that standard way is to pass it through View. Depending on how you instantiate your views, it could be DependencyProperty to bind in XAML, constructor parameter, or anything else. Then View passes it to it's ViewModel (pushes it to VM, not the way around: ViewModel should not be aware of View). This way you get a standalone closed component (your View), and external code does not know about it's internal implementation (which is ViewModel).
我相信标准的方法是通过 View 传递它。根据您实例化视图的方式,可能是 DependencyProperty 绑定到 XAML、构造函数参数或其他任何内容。然后 View 将它传递给它的 ViewModel(将它推送到 VM,而不是绕过:ViewModel 不应该知道 View)。这样你就得到了一个独立的封闭组件(你的视图),而外部代码不知道它的内部实现(即 ViewModel)。
In XAML it can be something like
在 XAML 中,它可能类似于
<ListBox x:Name="customers" />
<CustomerView Customer="{Binding SelectedItem, ElementName=customers}" />
And then in CustomerPropertyChanged handler you push value to the ViewModel.
然后在 CustomerPropertyChanged 处理程序中将值推送到 ViewModel。
回答by bland
So far I have considered the CustomerViewModel sending a request to the ViewAllCustomersViewModel once it has been instantiated (basically saying "I am ready to receive the message"), and then the ViewAllCustomersViewModel sending the ID back to the CustomerViewModel...
到目前为止,我已经考虑过 CustomerViewModel 在实例化后向 ViewAllCustomersViewModel 发送请求(基本上是说“我准备好接收消息”),然后 ViewAllCustomersViewModel 将 ID 发送回 CustomerViewModel ...
I would continue with this idea. It keeps the Views, ViewModels and Models all separate and unknowing of the others unlike other answers. Not to say other answers are wrong, or even bad, your option can be defined one or any of: personal preference, team convention, long-term MVVM goal of replacing components/modules, and complexity/ease of coding.
我会继续这个想法。与其他答案不同,它使 Views、ViewModels 和 Models 全部分开并且不知道其他人。并不是说其他答案是错误的,甚至是不好的,您的选项可以定义为以下一项或多项:个人偏好、团队约定、替换组件/模块的长期 MVVM 目标,以及编码的复杂性/易用性。
A side-effect to your idea I quoted above, which I prefer, is that you can request at any time as you've already set it up. So if you change whento perform that request very easily, or if you need to request updates, you use the same communication architecture in place.
我上面引用的你的想法的一个副作用,我更喜欢,是你可以随时请求,因为你已经设置了它。所以,如果你改变的时候非常容易地执行请求,或者如果你需要请求更新,您可以使用相同的通信架构到位。
Lastly, I prefer it because if you change your models or views or viewmodels - you keep the same core concept of communicating information between components.
最后,我更喜欢它,因为如果您更改模型或视图或视图模型 - 您将保持相同的组件之间通信信息的核心概念。

