Il framework Caliburn Micro, di cui ormai abbiamo parlato ampiamente con articoli e script, dispone di un servizio, rappresentato dall'interfaccia IEventAggregator, per inviare messaggi e gestirli tramite mediator pattern. Questa tecnica è utile quando chi manda un messaggio e chi lo riceve non si conoscono, in particolar modo quando il ViewModel deve comunicare con la View.
L'implementazione del framework permette l'invio di messaggi tramite il metodo Publish, mentre chi lo vuole processare deve implementare l'interfaccia IHandle<T> e sottoscrivere il messaggio chiamando il metodo Subscribe. Nel caso in cui mandiamo un messaggio per notificare l'utente, però, se vogliamo ricevere un'ipotetica scelta fatta sulla notifica, siamo obbligati a creare un nuovo messaggio di ritorno, che viene quindi lanciato dalla View e il ViewModel lo gestisce.
In questo script proponiamo un helper che ci facilita questo lavoro, con un apposito metodo PublishAsync, che ci permette di aspettare la risposta semplicemente usando async/await. Ponendo quindi di voler notificare l'utente e aspettare la risposta, quello che potremo scrivere sarà:
var r = await this.EventAggregator.PublishAsync(new AskMessage("Continuare?"));
Per farlo creiamo un extension metod di nome PublishAsync che estende IEventAggregator. Al suo interno lanciamo il messaggio e creiamo un handler specifico che gestirà il messaggio di ritorno.
private readonly static List<TaskCompleteHandler> handlers = new List<TaskCompleteHandler>(); public static async Task<R> PublishAsync<T, R>(this IEventAggregator eventAggregator, T message) where T : IDuplexMessage<R> { if (eventAggregator == null) throw new ArgumentNullException("eventAggregator"); if (message == null) throw new ArgumentNullException("message"); var handler = new TaskCompleteHandler(eventAggregator, message); handlers.Add(handler); eventAggregator.Subscribe(handler); eventAggregator.Publish(message); var r = await handler.Task; return (R)r; }
Possiamo vedere che richiediamo che il messaggio sia di tipo IDuplexMessage. E' un modo per tipizzare la chiamata, dato che l'interfaccia è vuota, e così definita.
public interface IDuplexMessage { } public interface IDuplexMessage<T> : IDuplexMessage { }
La classe TaskCompleteHandler è invece così definita.
internal class TaskCompleteHandler : IHandle<TaskCompletedMessage> { private readonly IEventAggregator eventAggregator; private readonly object originalMessage; private readonly TaskCompletionSource<object> taskSource; public TaskCompleteHandler(IEventAggregator eventAggregator, object message) { this.eventAggregator = eventAggregator; this.originalMessage = message; taskSource = new TaskCompletionSource<object>(); } public Task<object> Task { get { return taskSource.Task; } } public void Handle(TaskCompletedMessage message) { if (ReferenceEquals(message.OriginalMessage, this.originalMessage)) { handlers.Remove(this); eventAggregator.Unsubscribe(this); taskSource.TrySetResult(message.Result); } } }
Essa mantiene il riferimento al messaggio inviato e nel momento in cui riceve il messaggio di risposta, termina il TaskCompletionSource per completare l'operazione asincrona. Il messaggio di risposta generico è di tipo TaskCompletedMessage, così definito.
public class TaskCompletedMessage { public object OriginalMessage { get; private set; } public object Result { get; private set; } public TaskCompletedMessage(object originalMessage, object result) { OriginalMessage = originalMessage; Result = result; } }
Creiamo infine un secondo extension method di nome CompleteAsync, il quale riceve il messaggio originale da marcare come completato, e il risultato dell'operazione.
public static void CompleteAsync<T, R>(this IEventAggregator eventAggregator, T message, R result) where T : IDuplexMessage<R> { if (eventAggregator == null) throw new ArgumentNullException("eventAggregator"); if (message == null) throw new ArgumentNullException("message"); eventAggregator.Publish(new TaskCompletedMessage(message, result)); }
A questo punto abbiamo tutto per rendere funzionante il messaggio con la risposta. Chi gestisce il messaggio dovrà notificare il risultato con il seguente codice:
void IHandle<AskMessage>.Handle(AskMessage message) { var r = MessageBox.Show(message.Message, "Conferma", MessageBoxButton.OKCancel) == MessageBoxResult.OK; eventAggregator.CompleteAsync(message, r); }
Come possiamo vedere, una volta sviluppato l'helper, le chiamate da fare sono due: PublishAsync per lanciare il messaggio e CompleteAsync, per notificare la risposta.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Rinnovare il token di una GitHub App durante l'esecuzione di un workflow
Estrarre dati randomici da una lista di oggetti in C#
Utilizzare Tailwind CSS all'interno di React: installazione
Utilizzare QuickGrid di Blazor con Entity Framework
Le novità di Angular: i miglioramenti alla CLI
Sfruttare lo stream rendering per le pagine statiche di Blazor 8
Generare la software bill of material (SBOM) in GitHub
Paginare i risultati con QuickGrid in Blazor
Creare alias per tipi generici e tuple in C#
Sfruttare GPT-4o realtime su Azure Open AI per conversazioni vocali
Usare lo spread operator con i collection initializer in C#
Gestire domini wildcard in Azure Container Apps
I più letti di oggi
- La navigazione tra le pagine in Windows Phone 8.1
- Salvare in maniera sicura dati sensibili nelle Universal App con il Credential Locker
- Canary deployment con le pipeline YAML di Azure DevOps
- Registrare un applicazione per gestire l'auto upload in Windows Phone 8
- Tool per amministrare blob, queue e table di Windows Azure
- Creare un feed NuGet privato
- Avviare la mappa in modalità navigazione in Windows Phone 8
- Utilizzare Bootstrap per mostrare dei popup accanto a un controllo
- Building modern web apps with Blazor
- Adattare l'UI a risoluzioni differenti in Windows Phone 8.1