Hola, encontramos la solución a lo que buscas, has scroll y la encontrarás aquí.
Solución:
Editar: Esto no funciona bien para varios suscriptores, por lo que, a menos que solo tenga uno, no recomendaría usarlo.
Se siente un poco raro, pero nunca he encontrado nada mejor:
Declarar un delegado. Esto es identico a EventHandler
pero devuelve una tarea en lugar de void
public delegate Task AsyncEventHandler(object sender, EventArgs e);
Luego puede ejecutar lo siguiente y siempre que el controlador declarado en los usos principales async
y await
correctamente, entonces esto se ejecutará de forma asíncrona:
if (SearchRequest != null)
Debug.WriteLine("Starting...");
await SearchRequest(this, EventArgs.Empty);
Debug.WriteLine("Completed");
Manejador de muestras:
// declare handler for search request
myViewModel.SearchRequest += async (s, e) =>
await SearchOrders();
;
Nota: Nunca probé esto con varios suscriptores y no estoy seguro de cómo funcionará, así que si necesita varios suscriptores, asegúrese de probarlo con cuidado.
Según la respuesta de Simon_Weaver, creé una clase de ayuda que puede manejar varios suscriptores y tiene una sintaxis similar a los eventos de C#.
public class AsyncEvent where TEventArgs : EventArgs
private readonly List> invocationList;
private readonly object locker;
private AsyncEvent()
invocationList = new List>();
locker = new object();
public static AsyncEvent operator +(
AsyncEvent e, Func
Para usarlo, lo declaras en tu clase, por ejemplo:
public AsyncEvent SearchRequest;
Para suscribir un controlador de eventos, utilizará la sintaxis familiar (igual que en la respuesta de Simon_Weaver):
myViewModel.SearchRequest += async (s, e) =>
await SearchOrders();
;
Para invocar el evento, use el mismo patrón que usamos para los eventos de C# (solo con InvokeAsync):
var eventTmp = SearchRequest;
if (eventTmp != null)
await eventTmp.InvokeAsync(sender, eventArgs);
Si usa C # 6, uno debería poder usar el null operador condicional y escribe esto en su lugar:
await (SearchRequest?.InvokeAsync(sender, eventArgs) ?? Task.CompletedTask);
Los eventos no encajan perfectamente con async
y await
como has descubierto.
La forma en que se manejan las IU async
eventos es diferente de lo que está tratando de hacer. La interfaz de usuario proporciona una SynchronizationContext
a su async
eventos, lo que les permite reanudar en el subproceso de la interfaz de usuario. Lo hace no siempre “esperarlos”.
Mejor solución (IMO)
Creo que la mejor opción es construir el tuyo propio. async
-sistema pub/sub amigable, usando AsyncCountdownEvent
para saber cuándo se han completado todos los controladores.
Solución menor #1
async void
los métodos notifican a sus SynchronizationContext
cuándo comienzan y terminan (al aumentar/disminuir el recuento de operaciones asíncronas). Toda la interfaz de usuario SynchronizationContext
s ignorar estas notificaciones, pero usted pudo construya un contenedor que lo rastree y regrese cuando el conteo sea cero.
He aquí un ejemplo, usando AsyncContext
de mi biblioteca AsyncEx:
SearchCommand = new RelayCommand(() =>
IsSearching = true;
if (SearchRequest != null)
AsyncContext.Run(() => SearchRequest(this, EventArgs.Empty));
IsSearching = false;
);
Sin embargo, en este ejemplo, el subproceso de la interfaz de usuario es no bombeando mensajes mientras está en Run
.
Solución menor #2
También podrías hacer el tuyo SynchronizationContext
basado en un anidado Dispatcher
marco que aparece cuando el recuento de operaciones asincrónicas llega a cero. Sin embargo, luego introduce problemas de reingreso; DoEvents
se dejó fuera de WPF a propósito.
Si tienes alguna duda o capacidad de enriquecer nuestro post te inspiramos escribir un exégesis y con placer lo observaremos.