信使
介面 IMessenger
是類型合約,可用來在不同對象之間交換訊息。 這很適合用來分離應用程式的不同模組,而不需要保留所參考類型的強式參考。 您也可以將訊息傳送至令牌唯一識別的特定通道,並在應用程式的不同區段中有不同的 Messenger。 MVVM 工具組提供現成的兩個實作:和 StrongReferenceMessenger
:WeakReferenceMessenger
前者在內部使用弱式參考,為收件者提供自動記憶體管理,而後者則使用強式參考,並要求開發人員在不再需要收件者時手動取消訂閱收件者(如需有關如何取消註冊訊息處理程式的詳細數據,請參閱下方),但為了交換,提供更佳的效能,而且記憶體使用量要少得多。
平臺 API:、、
WeakReferenceMessenger
ObservableRecipient
StrongReferenceMessenger
IRecipient<TMessage>
MessageHandler<TRecipient, TMessage>
、RequestMessage<T>
、、、 。CollectionRequestMessage<T>
AsyncCollectionRequestMessage<T>
IMessenger
AsyncRequestMessage<T>
運作方式
實作 IMessenger
的類型負責維護收件者(郵件接收者)與其已註冊的郵件類型,以及相對訊息處理程式之間的連結。 任何物件都可以使用訊息處理程式註冊為指定郵件類型的收件者,每當 IMessenger
實例用來傳送該類型的訊息時,就會叫用這個物件。 您也可以透過特定通道傳送訊息(每個通道都是由唯一令牌識別),讓多個模組可以交換相同類型的訊息,而不會造成衝突。 未使用令牌傳送的訊息會使用預設共用通道。
有兩種方式可以執行訊息註冊:透過 IRecipient<TMessage>
介面,或使用做為訊息處理程式的 MessageHandler<TRecipient, TMessage>
委派。 第一個可讓您使用對延伸模組的單一呼叫 RegisterAll
來註冊所有處理程式,以自動註冊所有宣告的郵件處理程式的收件者,而當您需要更多彈性或想要使用簡單 Lambda 表達式做為訊息處理程式時,後者會很有用。
WeakReferenceMessenger
和 StrongReferenceMessenger
也會公開Default
屬性,提供內建的線程安全實作套件。 此外,您也可以視需要建立多個 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));
假設這個訊息類型用於簡單的訊息應用程式中,如果已選取,則會顯示標頭,其中包含目前已記錄使用者的使用者名稱和配置檔影像、具有交談清單的面板,以及另一個包含目前交談訊息的面板。 假設、 ConversationsListViewModel
和 ConversationViewModel
類型分別支援HeaderViewModel
這三個區段。 在此案例中, LoggedInUserChangedMessage
訊息可能會在登入作業完成之後傳送 HeaderViewModel
,而其他兩個 viewmodel 可能會為其註冊處理程式。 例如, 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>();