Посланник

Интерфейс IMessenger — это контракт для типов, которые можно использовать для обмена сообщениями между различными объектами. Это может быть полезно для разных модулей приложения без необходимости хранить надежные ссылки на типы, на которые ссылаются. Кроме того, можно отправлять сообщения в определенные каналы, однозначно идентифицироваться маркером и иметь разные посланники в разных разделах приложения. MVVM набор средств предоставляет две реализации из поля: и : ранее использует слабые ссылки внутри, WeakReferenceMessengerStrongReferenceMessengerпредлагая автоматическое управление памятью для получателей, в то время как последний использует надежные ссылки и требует, чтобы разработчики вручную отменяли подписку своих получателей, когда они больше не нужны (дополнительные сведения о том, как отменить регистрацию обработчиков сообщений можно найти ниже), но в обмен на это обеспечивает более высокую производительность и гораздо меньше памяти. Использования.

API платформы:IMessenger, WeakReferenceMessengerObservableRecipientRequestMessage<T>AsyncRequestMessage<T>IRecipient<TMessage>MessageHandler<TRecipient, TMessage>StrongReferenceMessenger, . CollectionRequestMessage<T>AsyncCollectionRequestMessage<T>

Как это работает

Реализация IMessenger типов отвечает за поддержание связей между получателями (получателями сообщений) и их зарегистрированными типами сообщений с относительными обработчиками сообщений. Любой объект можно зарегистрировать в качестве получателя для заданного типа сообщения с помощью обработчика сообщений, который будет вызываться всякий раз IMessenger , когда экземпляр используется для отправки сообщения этого типа. Кроме того, можно отправлять сообщения через определенные каналы связи (каждый из которых определяется уникальным маркером), чтобы несколько модулей могли обмениваться сообщениями одного типа без возникновения конфликтов. Сообщения, отправленные без маркера, используют общий канал по умолчанию.

Существует два способа регистрации сообщений: через интерфейс или с помощью делегата, действующего IRecipient<TMessage>MessageHandler<TRecipient, TMessage> в качестве обработчика сообщений. Первый позволяет зарегистрировать все обработчики с одним вызовом RegisterAll расширения, который автоматически регистрирует получателей всех объявленных обработчиков сообщений, в то время как последний полезен, если требуется больше гибкости или когда вы хотите использовать простое лямбда-выражение в качестве обработчика сообщений.

И то WeakReferenceMessenger , и другое StrongReferenceMessenger свойство, которое предлагает встроенную Default реализацию в пакет, безопасную для потоков. При необходимости можно также создать несколько экземпляров messenger, например, если другой экземпляр внедряется с поставщиком услуг DI в другой модуль приложения (например, несколько окон, работающих в одном процессе).

Примечание.

WeakReferenceMessenger Так как тип проще использовать и соответствует поведению типа messenger из MvvmLight библиотеки, он является типом по умолчанию, используемым ObservableRecipient типом в MVVM набор средств. Его StrongReferenceType можно использовать, передав экземпляр конструктору этого класса.

Отправка и получение сообщений

Рассмотрим следующий пример.

// Create a message
public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
    public LoggedInUserChangedMessage(User user) : base(user)
    {        
    }
}

// Register a message in some module
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this, (r, m) =>
{
    // Handle the message here, with r being the recipient and m being the
    // input message. Using the recipient passed as input makes it so that
    // the lambda expression doesn't capture "this", improving performance.
});

// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));

Предположим, что этот тип сообщения используется в простом приложении для обмена сообщениями, в котором отображается заголовок с именем пользователя и изображением профиля пользователя, зарегистрированного в настоящее время, панель со списком бесед и другой панелью с сообщениями из текущей беседы, если она выбрана. Предположим, что эти три раздела поддерживаются HeaderViewModelConversationsListViewModelConversationViewModel и типы соответственно. В этом сценарии LoggedInUserChangedMessage сообщение может быть отправлено HeaderViewModel после завершения операции входа, и оба других представления могут регистрировать обработчики для него. Например, ConversationsListViewModel загрузит список бесед для нового пользователя и ConversationViewModel просто закроет текущую беседу, если он присутствует.

Экземпляр IMessenger заботится о доставке сообщений всем зарегистрированным получателям. Обратите внимание, что получатель может подписаться на сообщения определенного типа. Обратите внимание, что унаследованные типы сообщений не регистрируются в реализациях по умолчаниюIMessenger, предоставляемых набор средств MVVM.

Если получатель больше не нужен, необходимо отменить регистрацию, чтобы он перестал получать сообщения. Вы можете отменить регистрацию по типу сообщения, маркеру регистрации или получателю:

// Unregisters the recipient from a message type
WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage>(this);

// Unregisters the recipient from a message type in a specified channel
WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage, int>(this, 42);

// Unregister the recipient from all messages, across all channels
WeakReferenceMessenger.Default.UnregisterAll(this);

Предупреждение

Как упоминание раньше, это не обязательно при использовании WeakReferenceMessenger типа, так как он использует слабые ссылки для отслеживания получателей, то есть неиспользуемые получатели по-прежнему будут иметь право на сборку мусора, хотя они по-прежнему имеют активные обработчики сообщений. Однако по-прежнему рекомендуется отменить их подписку, чтобы повысить производительность. С другой стороны, реализация StrongReferenceMessenger использует надежные ссылки для отслеживания зарегистрированных получателей. Это делается по соображениям производительности, и это означает, что каждый зарегистрированный получатель должен быть вручную отменен, чтобы избежать утечки памяти. То есть до тех пор, пока получатель зарегистрирован, StrongReferenceMessenger используемый экземпляр будет держать активную ссылку на него, что позволит сборщику мусора получить этот экземпляр. Вы можете обрабатывать это вручную или наследовать от ObservableRecipientнего, от которого по умолчанию автоматически выполняется удаление всех регистраций сообщений для получателя при отключении (дополнительные сведения см. в документации ObservableRecipient по этому параметру).

Кроме того, можно использовать IRecipient<TMessage> интерфейс для регистрации обработчиков сообщений. В этом случае каждому получателю потребуется реализовать интерфейс для заданного типа сообщения и указать Receive(TMessage) метод, который будет вызываться при получении сообщений, например:

// Create a message
public class MyRecipient : IRecipient<LoggedInUserChangedMessage>
{
    public void Receive(LoggedInUserChangedMessage message)
    {
        // Handle the message here...   
    }
}

// Register that specific message...
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this);

// ...or alternatively, register all declared handlers
WeakReferenceMessenger.Default.RegisterAll(this);

// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));

Использование сообщений запроса

Еще одна полезная функция экземпляров messenger заключается в том, что их также можно использовать для запроса значений из модуля в другой. Для этого пакет включает базовый RequestMessage<T> класс, который можно использовать следующим образом:

// Create a message
public class LoggedInUserRequestMessage : RequestMessage<User>
{
}

// Register the receiver in a module
WeakReferenceMessenger.Default.Register<MyViewModel, LoggedInUserRequestMessage>(this, (r, m) =>
{
    // Assume that "CurrentUser" is a private member in our viewmodel.
    // As before, we're accessing it through the recipient passed as
    // input to the handler, to avoid capturing "this" in the delegate.
    m.Reply(r.CurrentUser);
});

// Request the value from another module
User user = WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();

Класс RequestMessage<T> включает неявный преобразователь, который делает преобразование из содержащегося в нее LoggedInUserRequestMessageUser объекта возможным. Это также проверка, что ответ был получен для сообщения, и вызовет исключение, если это не так. Кроме того, можно отправлять сообщения запроса без этой обязательной гарантии ответа: просто хранить возвращенное сообщение в локальной переменной, а затем вручную проверка, доступно ли значение ответа. Это не приведет к активации автоматического исключения, если ответ не получен при возврате Send метода.

То же пространство имен также включает в себя сообщение базовых запросов для других сценариев: AsyncRequestMessage<T>CollectionRequestMessage<T> и AsyncCollectionRequestMessage<T>. Вот как можно использовать асинхронное сообщение запроса:

// Create a message
public class LoggedInUserRequestMessage : AsyncRequestMessage<User>
{
}

// Register the receiver in a module
WeakReferenceMessenger.Default.Register<MyViewModel, LoggedInUserRequestMessage>(this, (r, m) =>
{
    m.Reply(r.GetCurrentUserAsync()); // We're replying with a Task<User>
});

// Request the value from another module (we can directly await on the request)
User user = await WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();

Примеры

  • Ознакомьтесь с примером приложения (для нескольких платформ пользовательского интерфейса), чтобы просмотреть набор средств MVVM в действии.
  • Дополнительные примеры можно найти в модульных тестах.