信使

介面 IMessenger 是類型合約,可用來在不同對象之間交換訊息。 這有助於在不必對所參照的型別保持強參考的情況下,解耦應用程式中的不同模組。 您也可以將訊息傳送到由權杖唯一識別的特定頻道,並在應用程式的不同區段使用不同的訊息傳遞器。 MVVM 工具組內建提供兩種實作:WeakReferenceMessengerStrongReferenceMessenger:前者在內部使用弱參考,為接收者提供自動記憶體管理;後者則使用強參考,並要求開發人員在不再需要其接收者時手動取消訂閱(如需了解如何取消註冊訊息處理常式的詳細資訊,請參閱下文),但相對地可提供更佳的效能,且記憶體用量也少得多。

平台 API:IMessengerWeakReferenceMessengerStrongReferenceMessengerIRecipient<TMessage>MessageHandler<TRecipient, TMessage>ObservableRecipientRequestMessage<T>AsyncRequestMessage<T>CollectionRequestMessage<T>AsyncCollectionRequestMessage<T>

運作方式

實作 IMessenger 的類型負責維護收件者(郵件接收者)與其已註冊的郵件類型,以及相對訊息處理程式之間的連結。 任何物件都可以使用訊息處理程式註冊為指定郵件類型的收件者,每當 IMessenger 實例用來傳送該類型的訊息時,就會叫用這個物件。 您也可以透過特定通道傳送訊息(每個通道都是由唯一令牌識別),讓多個模組可以交換相同類型的訊息,而不會造成衝突。 未使用令牌傳送的訊息會使用預設共用通道。

有兩種方式可以執行訊息註冊:透過 IRecipient<TMessage> 介面,或使用做為訊息處理程式的 MessageHandler<TRecipient, TMessage> 委派。 第一個可讓您使用對延伸模組的單一呼叫 RegisterAll 來註冊所有處理程式,以自動註冊所有宣告的郵件處理程式的收件者,而當您需要更多彈性或想要使用簡單 Lambda 表達式做為訊息處理程式時,後者會很有用。

WeakReferenceMessengerStrongReferenceMessenger 也會公開Default屬性,提供內建的線程安全實作套件。 此外,如有需要,您也可以建立多個 Messenger 執行個體,例如,透過 DI 服務提供者將不同的 Messenger 執行個體注入應用程式的不同模組中(例如,在同一個處理程序中執行的多個視窗)。

注意

由於 WeakReferenceMessenger 類型較容易使用,且其行為與 MvvmLight 程式庫中的 messenger 類型相符,因此在 MVVM Toolkit 中,它是 ObservableRecipient 類型所使用的預設類型。 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));

讓我們想像這種訊息類型被用在一個簡單的訊息應用程式中,該程式會顯示一個標頭,內含目前登入使用者的使用者名稱和個人資料圖片、一個列出對話清單的面板,以及在已選取某個對話時顯示目前對話訊息的另一個面板。 假設、 HeaderViewModelConversationsListViewModel 類型分別支援ConversationViewModel這三個區段。 在這種情況下,LoggedInUserChangedMessage 訊息可能會由 HeaderViewModel 在登入作業完成後送出,而另外兩個檢視模型都可能會為它註冊處理常式。 例如,ConversationsListViewModel 將會載入新使用者的交談清單,而 ConversationViewModel 則只會在目前有交談時關閉目前的交談。

實例 IMessenger 會負責傳遞訊息給所有已註冊的收件者。 請注意,收件者可以訂閱特定類型的訊息。 請注意,繼承的訊息類型不會在MVVM Toolkit所提供的預設 IMessenger 實作中註冊。

當不再需要收件者時,您應該將其取消註冊,使其停止接收訊息。 您可以透過訊息類型、註冊令牌或收件者取消註冊:

// 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> 類別包含隱含轉換運算子,讓從 LoggedInUserRequestMessage 轉換為其所包含的 User 物件成為可能。 這也會檢查是否已收到該訊息的回應,若未收到,則會擲出例外。 您也可以在沒有這項強制回應保證的情況下傳送要求訊息:只要將傳回的訊息儲存在局部變數中,然後手動檢查回應值是否可用。 如果在 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>();

範例

  • 查看 範例應用程式 (適用於多個 UI 架構),以查看 MVVM 工具組的運作情形。
  • 您也可以在單元測試中找到更多範例。