Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A IMessenger interface é um contrato para tipos que podem ser usados para trocar mensagens entre diferentes objetos. Isto pode ser útil para desacoplar diferentes módulos de uma aplicação sem ter de manter referências fortes aos tipos referenciados. Também é possível enviar mensagens para canais específicos, identificados unicamente por um token, e ter diferentes mensageiros em diferentes secções de uma aplicação. O MVVM Toolkit fornece duas implementações logo de fábrica: WeakReferenceMessenger e StrongReferenceMessenger: a primeira usa referências fracas internamente, oferecendo gestão automática de memória para os destinatários, enquanto a segunda usa referências fortes e exige que os programadores cancelem manualmente a subscrição dos destinatários quando já não são necessários (mais detalhes sobre como cancelar o registo dos manipuladores de mensagens podem ser encontrados abaixo), mas em troca disso oferece melhor desempenho e muito menos uso de memória.
APIs da plataforma:
IMessenger,WeakReferenceMessenger,StrongReferenceMessenger,IRecipient<TMessage>,MessageHandler<TRecipient, TMessage>ObservableRecipient,RequestMessage<T>,AsyncRequestMessage<T>.CollectionRequestMessage<T>AsyncCollectionRequestMessage<T>
Como funciona
Os tipos que implementam IMessenger são responsáveis por manter as associações entre os destinatários (recetores de mensagens) e os tipos de mensagem que registaram, com os respetivos manipuladores de mensagens. Qualquer objeto pode ser registado como destinatário para um dado tipo de mensagem usando um manipulador de mensagens, que será invocado sempre que a IMessenger instância for usada para enviar uma mensagem desse tipo. Também é possível enviar mensagens através de canais de comunicação específicos (cada um identificado por um token único), de modo que múltiplos módulos possam trocar mensagens do mesmo tipo sem causar conflitos. Mensagens enviadas sem token utilizam o canal partilhado predefinido.
Existem duas formas de realizar o registo de mensagens: ou através da IRecipient<TMessage> interface, ou usando um MessageHandler<TRecipient, TMessage> delegado a atuar como manipulador de mensagens. A primeira permite registar todos os manipuladores com uma única chamada à RegisterAll extensão, que regista automaticamente os destinatários de todos os manipuladores de mensagens declarados, enquanto a segunda é útil quando precisa de mais flexibilidade ou quando quer usar uma expressão lambda simples como tratador de mensagens.
Ambos WeakReferenceMessenger e StrongReferenceMessenger também disponibilizam a propriedade Default, que oferece uma implementação segura para threads integrada no pacote. Também é possível criar múltiplas instâncias de mensageiro, se necessário, por exemplo, se uma diferente for injetada com um fornecedor de serviços DI num módulo diferente da aplicação (por exemplo, várias janelas a correr no mesmo processo).
Observação
Como o WeakReferenceMessenger tipo é mais simples de usar e corresponde ao comportamento do tipo messenger da MvvmLight biblioteca, é o tipo padrão usado pelo ObservableRecipient tipo no MVVM Toolkit. O StrongReferenceType ainda pode ser usado, passando uma instância ao construtor dessa classe.
Enviar e receber mensagens
Considere o seguinte:
// 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));
Vamos imaginar este tipo de mensagem a ser usado numa aplicação simples de mensagens, que mostra um cabeçalho com o nome de utilizador e a imagem de perfil do utilizador atualmente registado, um painel com uma lista de conversas e outro painel com mensagens da conversa atual, se estiver selecionado. Imaginemos que estas três secções são suportadas pelos HeaderViewModeltipos , ConversationsListViewModel e ConversationViewModel respetivamente. Neste cenário, a mensagem LoggedInUserChangedMessage pode ser enviada pelo HeaderViewModel após a conclusão de uma operação de início de sessão, e esses dois outros viewmodels podem registar processadores para ela. Por exemplo, ConversationsListViewModel carrega a lista de conversas do novo utilizador e ConversationViewModel fecha apenas a conversa atual, se houver uma conversa presente.
A IMessenger instância trata da entrega das mensagens a todos os destinatários registados. Note que um destinatário pode subscrever mensagens de um tipo específico. Note que os tipos de mensagens herdados não estão registados nas implementações padrão IMessenger fornecidas pelo MVVM Toolkit.
Quando um destinatário deixar de ser necessário, deve anular o seu registo para que deixe de receber mensagens. Pode cancelar o registo por tipo de mensagem, por token de registo ou por destinatário:
// 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);
Warning
Como mencionado anteriormente, isto não é estritamente necessário ao usar o WeakReferenceMessenger tipo, pois utiliza referências fracas para rastrear destinatários, o que significa que os destinatários não utilizados continuarão elegíveis para recolha de lixo mesmo tendo manipuladores de mensagens ativos. Ainda assim, é boa prática cancelá-los para melhorar o desempenho. Por outro lado, a StrongReferenceMessenger implementação utiliza referências fortes para rastrear os destinatários registados. Isto é feito por razões de desempenho, e significa que cada destinatário registado deve ser manualmente desregistado para evitar fugas de memória. Ou seja, enquanto um destinatário estiver registado, a StrongReferenceMessenger instância em uso manterá uma referência ativa a ela, o que impedirá o coletor de lixo de recolher essa instância. Pode tratar disto manualmente, ou pode herdar de ObservableRecipient, que, por defeito, remove automaticamente todos os registos de mensagens do destinatário quando é desativado (consulte a documentação sobre ObservableRecipient para obter mais informações sobre isto).
Também é possível usar a IRecipient<TMessage> interface para registar manipuladores de mensagens. Neste caso, cada destinatário terá de implementar a interface para um dado tipo de mensagem e fornecer um Receive(TMessage) método que será invocado ao receber mensagens, da seguinte forma:
// 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));
Utilização de mensagens de pedido
Outra funcionalidade útil das instâncias de mensageiro é que também podem ser usadas para solicitar valores de um módulo para outro. Para tal, o pacote inclui uma classe base RequestMessage<T> , que pode ser usada da seguinte forma:
// 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>();
A RequestMessage<T> classe inclui um conversor implícito que torna possível a conversão de a LoggedInUserRequestMessage para o seu objeto contido User . Isto também verificará se foi recebida uma resposta para a mensagem e lançará uma exceção se não for esse o caso. Também é possível enviar mensagens de pedido sem esta garantia obrigatória de resposta: basta guardar a mensagem devolvida numa variável local e depois verificar manualmente se existe ou não um valor de resposta. Ao fazê-lo, não será acionada a exceção automática se não for recebida uma resposta quando o método Send regressar.
O mesmo espaço de nomes inclui também mensagens de pedidos base para outros cenários: AsyncRequestMessage<T>, CollectionRequestMessage<T> e AsyncCollectionRequestMessage<T>.
Aqui está como pode usar uma mensagem de pedido assíncrono:
// 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>();
Exemplos
- Dá uma vista de olhos à aplicação de exemplo (para múltiplos frameworks de interface) para veres o MVVM Toolkit em ação.
- Também podes encontrar mais exemplos nos testes unitários.