视图模型之间的 WPF MVVM 通信

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23798425/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 11:46:04  来源:igfitidea点击:

WPF MVVM communication between View Model

c#wpfmvvmviewmodel

提问by user1662008

I am working on WPF MVVM application wherein I have 2 views View1 and View2 with their respective ViewModels. Now, I want on click of a button in View1 would close View1 and open View2 using ViewModel1. Also, I want to pass some data say a instance of person class to ViewModel2 when opening from ViewModel1 which would be used to display information in View2.

我正在开发 WPF MVVM 应用程序,其中我有 2 个视图 View1 和 View2 以及它们各自的 ViewModel。现在,我想单击 View1 中的按钮将关闭 View1 并使用 ViewModel1 打开 View2。另外,我想在从 ViewModel1 打开时将一些数据(例如 person 类的实例)传递给 ViewModel2,该数据将用于在 View2 中显示信息。

What is the best and possibly the simplest way to achieve this inside ViewModels only, I would want to avoid writing code for navigation in code behind.

仅在 ViewModels 中实现此目的的最佳且可能最简单的方法是什么,我想避免在后面的代码中编写导航代码。

采纳答案by BatteryBackupUnit

How about using the Mediator pattern (for example see technical-recipes.comor John Smith) or weak events? Afaik several MVVM frameworks/libs (like PRISM, Caliburn.Micro, MVVMCross) already come with the infrastructure code for these. There are also separate libraries that are independent of any specific MVVM framework, like AppccelerateEventBrokerwhich can help you achieve something along the lines of what you want.

使用 Mediator 模式(例如,请参阅tech-recipes.comJohn Smith)或弱事件如何?Afaik 几个 MVVM 框架/库(如 PRISM、Caliburn.Micro、MVVMCross)已经带有这些基础设施代码。还有独立于任何特定 MVVM 框架的独立库,例如Appccelerate EventBroker,它可以帮助您实现您想要的东西。

With events, however, I wonder whether you require some feedback on whether the event was "correctly" handled or not. There are ways to achieve this (altering the value of the event args, handling the events sync, after raising the event, checking the value of the event args), but they are not as concise as a method's return value or a method throwing an exception.

但是,对于事件,我想知道您是否需要一些关于事件是否“正确”处理的反馈。有一些方法可以实现这一点(改变事件 args 的值,处理事件同步,在引发事件之后,检查事件 args 的值),但它们不像方法的返回值或抛出一个方法那么简洁例外。

EDIT: sorry I just realized that the second view/ViewModel is not open, yet. So my "solution" is not (that simply) applicable. You need to pass the instruction "up" in the view model tree, maybe even to the root, where you can instantiate and show the new view model (show in a new window or as a ContentControl in an existing view?)

编辑:抱歉,我刚刚意识到第二个视图/ViewModel 尚未打开。所以我的“解决方案”并不(那么简单)适用。您需要在视图模型树中传递“向上”指令,甚至可能传递到根,在那里您可以实例化并显示新的视图模型(在新窗口中显示或作为现有视图中的 ContentControl 显示?)

回答by Dalstroem

I created this Messengerclass to handle communication between ViewModels.

我创建了这个Messenger类来处理 ViewModel 之间的通信。

Register for an added person object in MainViewModel:

在 中注册添加的人员对象MainViewModel

Messenger.Default.Register<Person>(this, AddPersonToCollection, Context.Added);

To notify all registered ViewModels about the added person from the CreatePersonViewModel:

从以下位置通知所有已注册的 ViewModel 有关添加的人员CreatePersonViewModel

Messenger.Default.Send(person, Context.Added);

Source code:

源代码:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Application.Messaging
{
    public class Messenger
    {
        private static readonly object CreationLock = new object();
        private static readonly ConcurrentDictionary<MessengerKey, object> Dictionary = new ConcurrentDictionary<MessengerKey, object>();

        #region Default property

        private static Messenger _instance;

        /// <summary>
        /// Gets the single instance of the Messenger.
        /// </summary>
        public static Messenger Default
        {
            get
            {
                if (_instance == null)
                {
                    lock (CreationLock)
                    {
                        if (_instance == null)
                        {
                            _instance = new Messenger();
                        }
                    }
                }

                return _instance;
            }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the Messenger class.
        /// </summary>
        private Messenger()
        {
        }

        /// <summary>
        /// Registers a recipient for a type of message T. The action parameter will be executed
        /// when a corresponding message is sent.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="recipient"></param>
        /// <param name="action"></param>
        public void Register<T>(object recipient, Action<T> action)
        {
            Register(recipient, action, null);
        }

        /// <summary>
        /// Registers a recipient for a type of message T and a matching context. The action parameter will be executed
        /// when a corresponding message is sent.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="recipient"></param>
        /// <param name="action"></param>
        /// <param name="context"></param>
        public void Register<T>(object recipient, Action<T> action, object context)
        {
            var key = new MessengerKey(recipient, context);
            Dictionary.TryAdd(key, action);
        }

        /// <summary>
        /// Unregisters a messenger recipient completely. After this method is executed, the recipient will
        /// no longer receive any messages.
        /// </summary>
        /// <param name="recipient"></param>
        public void Unregister(object recipient)
        {
            Unregister(recipient, null);
        }

        /// <summary>
        /// Unregisters a messenger recipient with a matching context completely. After this method is executed, the recipient will
        /// no longer receive any messages.
        /// </summary>
        /// <param name="recipient"></param>
        /// <param name="context"></param>
        public void Unregister(object recipient, object context)
        {
            object action;
            var key = new MessengerKey(recipient, context);
            Dictionary.TryRemove(key, out action);
        }

        /// <summary>
        /// Sends a message to registered recipients. The message will reach all recipients that are
        /// registered for this message type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="message"></param>
        public void Send<T>(T message)
        {
            Send(message, null);
        }

        /// <summary>
        /// Sends a message to registered recipients. The message will reach all recipients that are
        /// registered for this message type and matching context.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="message"></param>
        /// <param name="context"></param>
        public void Send<T>(T message, object context)
        {
            IEnumerable<KeyValuePair<MessengerKey, object>> result;

            if (context == null)
            {
                // Get all recipients where the context is null.
                result = from r in Dictionary where r.Key.Context == null select r;
            }
            else
            {
                // Get all recipients where the context is matching.
                result = from r in Dictionary where r.Key.Context != null && r.Key.Context.Equals(context) select r;
            }

            foreach (var action in result.Select(x => x.Value).OfType<Action<T>>())
            {
                // Send the message to all recipients.
                action(message);
            }
        }

        protected class MessengerKey
        {
            public object Recipient { get; private set; }
            public object Context { get; private set; }

            /// <summary>
            /// Initializes a new instance of the MessengerKey class.
            /// </summary>
            /// <param name="recipient"></param>
            /// <param name="context"></param>
            public MessengerKey(object recipient, object context)
            {
                Recipient = recipient;
                Context = context;
            }

            /// <summary>
            /// Determines whether the specified MessengerKey is equal to the current MessengerKey.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            protected bool Equals(MessengerKey other)
            {
                return Equals(Recipient, other.Recipient) && Equals(Context, other.Context);
            }

            /// <summary>
            /// Determines whether the specified MessengerKey is equal to the current MessengerKey.
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                if (ReferenceEquals(this, obj)) return true;
                if (obj.GetType() != GetType()) return false;

                return Equals((MessengerKey)obj);
            }

            /// <summary>
            /// Serves as a hash function for a particular type. 
            /// </summary>
            /// <returns></returns>
            public override int GetHashCode()
            {
                unchecked
                {
                    return ((Recipient != null ? Recipient.GetHashCode() : 0) * 397) ^ (Context != null ? Context.GetHashCode() : 0);
                }
            }
        }
    }
}

回答by AnjumSKhan

Use a tiny dedicated Light Message Bus. It is not a part of any MVVMframework, so one can use it independently. Very very easy to install and use.

使用一个很小的专用Light Message Bus。它不是任何MVVM框架的一部分,因此可以独立使用它。非常非常容易安装和使用。

Usage guidelines

使用指南