How to 'await' raising an EventHandler event

Sometimes the event pattern is used to raise events in MVVM applications by or a child viewmodel to send a message to its parent viewmodel in a loosely coupled way like this.

Parent ViewModel

searchWidgetViewModel.SearchRequest += (s,e) => 
{
    SearchOrders(searchWidgitViewModel.SearchCriteria);
};

SearchWidget ViewModel

public event EventHandler SearchRequest;

SearchCommand = new RelayCommand(() => {

    IsSearching = true;
    if (SearchRequest != null) 
    {
        SearchRequest(this, EventArgs.Empty);
    }
    IsSearching = false;
});

In refactoring my application for .NET4.5 I am making as much as code possible to use async and await. However the following doesn't work (well I really wasn't expecting it to)

 await SearchRequest(this, EventArgs.Empty);

The framework definitely does this to call event handlers such as this, but I'm not sure how it does it?

private async void button1_Click(object sender, RoutedEventArgs e)
{
   textBlock1.Text = "Click Started";
   await DoWork();
   textBlock2.Text = "Click Finished";
}

Anything I've found on the subject of raising events asynchrously is ancient but I can't find something in the framework to support this.

How can I await the calling of an event but remain on the UI thread.


Edit: This doesn't work well for multiple subscribers, so unless you only have one I wouldn't recommend using this.


Feels slightly hacky - but I have never found anything better:

Declare a delegate. This is identical to EventHandler but returns a task instead of void

public delegate Task AsyncEventHandler(object sender, EventArgs e);

You can then run the following and as long as the handler declared in the parent uses async and await properly then this will run asynchronously:

if (SearchRequest != null) 
{
    Debug.WriteLine("Starting...");
    await SearchRequest(this, EventArgs.Empty);
    Debug.WriteLine("Completed");
}

Sample handler:

 // declare handler for search request
 myViewModel.SearchRequest += async (s, e) =>
 {                    
     await SearchOrders();
 };

Note: I've never tested this with multiple subscribers and not sure how this will work - so if you need multiple subscribers then make sure to test it carefully.


Based on Simon_Weaver's answer, I created a helper class that can handle multiple subscribers, and has a similar syntax to c# events.

public class AsyncEvent<TEventArgs> where TEventArgs : EventArgs
{
    private readonly List<Func<object, TEventArgs, Task>> invocationList;
    private readonly object locker;

    private AsyncEvent()
    {
        invocationList = new List<Func<object, TEventArgs, Task>>();
        locker = new object();
    }

    public static AsyncEvent<TEventArgs> operator +(
        AsyncEvent<TEventArgs> e, Func<object, TEventArgs, Task> callback)
    {
        if (callback == null) throw new NullReferenceException("callback is null");

        //Note: Thread safety issue- if two threads register to the same event (on the first time, i.e when it is null)
        //they could get a different instance, so whoever was first will be overridden.
        //A solution for that would be to switch to a public constructor and use it, but then we'll 'lose' the similar syntax to c# events             
        if (e == null) e = new AsyncEvent<TEventArgs>();

        lock (e.locker)
        {
            e.invocationList.Add(callback);
        }
        return e;
    }

    public static AsyncEvent<TEventArgs> operator -(
        AsyncEvent<TEventArgs> e, Func<object, TEventArgs, Task> callback)
    {
        if (callback == null) throw new NullReferenceException("callback is null");
        if (e == null) return null;

        lock (e.locker)
        {
            e.invocationList.Remove(callback);
        }
        return e;
    }

    public async Task InvokeAsync(object sender, TEventArgs eventArgs)
    {
        List<Func<object, TEventArgs, Task>> tmpInvocationList;
        lock (locker)
        {
            tmpInvocationList = new List<Func<object, TEventArgs, Task>>(invocationList);
        }

        foreach (var callback in tmpInvocationList)
        {
            //Assuming we want a serial invocation, for a parallel invocation we can use Task.WhenAll instead
            await callback(sender, eventArgs);
        }
    }
}

To use it, you declare it in your class, for example:

public AsyncEvent<EventArgs> SearchRequest;

To subscribe an event handler, you'll use the familiar syntax (the same as in Simon_Weaver's answer):

myViewModel.SearchRequest += async (s, e) =>
{                    
   await SearchOrders();
};

To invoke the event, use the same pattern we use for c# events (only with InvokeAsync):

var eventTmp = SearchRequest;
if (eventTmp != null)
{
   await eventTmp.InvokeAsync(sender, eventArgs);
}

If using c# 6, one should be able to use the null conditional operator and write this instead:

await (SearchRequest?.InvokeAsync(sender, eventArgs) ?? Task.CompletedTask);