web-dev-qa-db-ja.com

ビューモデル間のWPFMVVM通信

私はWPFMVVMアプリケーションに取り組んでおり、View1とView2の2つのビューとそれぞれのViewModelがあります。ここで、View1のボタンをクリックすると、ViewModel1を使用してView1が閉じ、View2が開きます。また、View2で情報を表示するために使用されるViewModel1から開くときに、personクラスのインスタンスなどのデータをViewModel2に渡したいと思います。

ViewModel内でのみこれを実現するための最良かつおそらく最も簡単な方法は何ですか、コードビハインドでナビゲーション用のコードを書くことは避けたいと思います。

11
user1662008

Mediatorパターン(たとえば、 technical-recipes.com または John Smith を参照)または弱いイベントを使用するのはどうですか? AfaikのいくつかのMVVMフレームワーク/ライブラリ(PRISM、Caliburn.Micro、MVVMCrossなど)には、これらのインフラストラクチャコードがすでに付属しています。 AppccelerateEventBroker のように、特定のMVVMフレームワークから独立した個別のライブラリもあります。これは、目的に沿って何かを達成するのに役立ちます。

ただし、イベントの場合、イベントが「正しく」処理されたかどうかについてフィードバックが必要かどうか疑問に思います。これを実現する方法はいくつかあります(イベント引数の値の変更、イベント同期の処理、イベントの発生後、イベント引数の値の確認)が、メソッドの戻り値やメソッドがスローするほど簡潔ではありません。例外。

編集:申し訳ありませんが、2番目のビュー/ ViewModelがまだ開いていないことに気づきました。したがって、私の「解決策」は(単純に)適用できません。ビューモデルツリーの「上」命令を、場合によってはルートに渡す必要があります。ルートでは、新しいビューモデルをインスタンス化して表示できます(新しいウィンドウに表示するか、既存のビューにContentControlとして表示しますか?)

4

このMessengerクラスを作成して、ViewModel間の通信を処理しました。

追加された人物オブジェクトをMainViewModelに登録します。

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

CreatePersonViewModelから追加された人物について、登録されているすべてのViewModelに通知するには:

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

ソースコード:

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);
                }
            }
        }
    }
}
26
Dalstroem

小さな専用を使用してください ライトメッセージバス 。これは[〜#〜] mvvm [〜#〜]フレームワークの一部ではないため、独立して使用できます。インストールと使用が非常に簡単です。

使用ガイドライン

1
AnjumSKhan