Связь WPF MVVM между моделью представления
Я работаю над приложением WPF MVVM, в котором у меня есть 2 представления View1 и View2 с их соответствующими моделями представлений. Теперь я хочу, чтобы при нажатии кнопки в View1 закрыть View1 и открыть View2 с помощью ViewModel1. Кроме того, я хочу передать некоторые данные, скажем, экземпляр класса person в ViewModel2 при открытии из ViewModel1, который будет использоваться для отображения информации в View2.
каков наилучший и, возможно, самый простой способ достичь этого только внутри ViewModels, я хотел бы избегайте написания кода для навигации в коде сзади.
3 ответов
Как насчет использования шаблона посредника (например, см. technical-recipes.com или Джон Смит) или слабых событий? Afaik несколько рамок MVVM / libs (например, PRISM, Caliburn.Micro, MVVMCross) уже поставляются с кодом инфраструктуры для них. Есть также отдельные библиотеки, которые не зависят от какой-либо конкретной структуры mvvm, например Appccelerate EventBroker, который может помочь вам достичь чего-то вдоль линий, что вы хотеть.
с событиями, однако, мне интересно, требуется ли вам некоторая обратная связь, было ли событие "правильно" обработано или нет. Есть способы достичь этого (изменение значения args события, обработка синхронизации событий, после вызова события, проверка значения args события), но они не так лаконичны, как возвращаемое значение метода или метод, вызывающий исключение.
EDIT: извините, я только что понял,что второй вид / viewmodel еще не открыт. Так что мое "решение" не (это просто) применимо. Вам нужно передать инструкцию "вверх" в дереве модели представления, возможно, даже в корень, где вы можете создать экземпляр и показать новую модель представления (показать в новом окне или как ContentControl в существующем представлении?)
Я создал этот Messenger
класс для обработки связи между ViewModels.
Регистрация добавленного объекта person в MainViewModel
:
Messenger.Default.Register<Person>(this, AddPersonToCollection, Context.Added);
чтобы уведомить всех зарегистрированных ViewModels о добавленном человеке из CreatePersonViewModel
:
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);
}
}
}
}
}
используйте крошечный выделенный Светлая Шина Сообщения. Это не является частью любого MVVM framework, поэтому его можно использовать независимо. Очень прост в установке и использовании.