C# WPF MVVM与Messenger的通信(VM的邮件加载)
背景 我正在使用MVVM模式编写WPF应用程序。我正在使用Messenger在ViewModels之间进行通信,这是我在各种教程中学习到的。我正在使用本文代码部分中的Messenger类的实现(感谢Pluralsight上的@Dalstroem和Gill Cleeren) 由于我的应用程序需要大量的视图/虚拟机,因此在需要视图时会实例化每个视图模型,并随后进行处理(视图优先,虚拟机指定为视图的DataContext) 问题C# WPF MVVM与Messenger的通信(VM的邮件加载),c#,wpf,mvvm,communication,messenger,C#,Wpf,Mvvm,Communication,Messenger,背景 我正在使用MVVM模式编写WPF应用程序。我正在使用Messenger在ViewModels之间进行通信,这是我在各种教程中学习到的。我正在使用本文代码部分中的Messenger类的实现(感谢Pluralsight上的@Dalstroem和Gill Cleeren) 由于我的应用程序需要大量的视图/虚拟机,因此在需要视图时会实例化每个视图模型,并随后进行处理(视图优先,虚拟机指定为视图的DataContext) 问题 public class Messenger { private
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);
}
}
}
}
每个ViewModel的构造函数根据需要加载资源(命令、服务等),并注册感兴趣的消息。从以前存在的ViewModels发送的消息不会被新的ViewModels拾取
因此,我无法使用Messenger类在ViewModels之间进行通信
思想
我看到的一些示例使用ViewModelLocator预先实例化所有ViewModel。创建视图时,只需从VML中提取现有的ViewModel。这种方法意味着消息将始终在每个ViewModel中接收并可用。我担心的是,30多个ViewModels在使用时都会加载大量数据,随着每个视图的使用,我的应用程序在扩展使用时会变得很慢(从来没有配置过资源)
我考虑过找到一种存储邮件的方法,然后将所有邮件重新发送给任何已注册的收件人。如果实现,这将允许我在每个ViewModel中注册消息后调用重发排序方法。我对这种方法有一些担忧,包括随着时间的推移信息的积累
我不确定我做错了什么,也不确定是否有我不知道的方法
代码
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);
}
}
}
}
公共类信使
{
私有静态只读对象CreationLock=新对象();
私有静态只读ConcurrentDictionary=新ConcurrentDictionary();
#区域默认属性
私有静态Messenger_实例;
///
///获取Messenger的单个实例。
///
公共静态信使默认值
{
得到
{
if(_instance==null)
{
锁定(CreationLock)
{
if(_instance==null)
{
_实例=新的Messenger();
}
}
}
返回_实例;
}
}
#端区
///
///初始化Messenger类的新实例。
///
私人信使()
{
}
///
///为一种类型的邮件T注册收件人。将执行操作参数
///当发送相应的消息时。
///
///
///
///
公共无效登记簿(对象收件人,操作)
{
登记簿(收件人、操作、空);
}
///
///为邮件类型T和匹配上下文注册收件人。将执行操作参数
///当发送相应的消息时。
///
///
///
///
///
公共无效寄存器(对象收件人、操作、对象上下文)
{
var key=新的信使密钥(收件人、上下文);
TryAdd(键,动作);
}
///
///完全注销messenger收件人。执行此方法后,收件人将
///不再接收任何消息。
///
///
公共作废注销(对象收件人)
{
取消注册(收件人,空);
}
///
///使用匹配的上下文完全注销messenger收件人。执行此方法后,收件人将
///不再接收任何消息。
///
///
///
公共作废注销(对象收件人、对象上下文)
{
客体行为;
var key=新的信使密钥(收件人、上下文);
TryRemove(key,out动作);
}
///
///向已注册的收件人发送邮件。该邮件将送达所有已注册的收件人
///已为此邮件类型注册。
///
///
///
公共无效发送(T消息)
{
发送(消息,空);
}
///
///向已注册的收件人发送邮件。该邮件将送达所有已注册的收件人
///已为此消息类型和匹配上下文注册。
///
///
///
///
公共void发送(T消息、对象上下文)
{
可数结果;
if(上下文==null)
{
//获取上下文为空的所有收件人。
结果=来自字典中的r,其中r.Key.Context==null选择r;
}
其他的
{
//获取上下文匹配的所有收件人。
result=来自字典中的r,其中r.Key.Context!=null&&r.Key.Context.Equals(Context)选择r;
}
foreach(result.Select(x=>x.Value).OfType()中的var操作)
{
//将邮件发送给所有收件人。
行动(信息);
}
}
受保护类信使密钥
{
公共对象收件人{get;private set;}
公共对象上下文{get;private set;}
///
///初始化MessengerKey类的新实例。
///
///
///
公用信使密钥(对象收件人、对象上下文)
{
收件人=收件人;
上下文=上下文;
}
///
///确定指定的Messenger密钥是否等于当前Messenger密钥。
///
///
///
受保护的bool等于(信使键其他)
{
返回等于(Recipient,other.Recipient)和等于(Context,other.Context);
}
///
///确定指定的Messenger密钥是否等于当前值