Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 980
Simon_Weaver Avatar asked Sep 16 '12 23:09

Simon_Weaver


People also ask

Which async method can be used for event handlers?

Since delegates (and events are delegates) implement the Asynchronous Programming Model (APM), you could use the TaskFactory. FromAsync method.

How do you declare an EventHandler?

Use the EventHandler delegate for all events that do not include event data. Use the EventHandler<TEventArgs> delegate for events that include data about the event. These delegates have no return type value and take two parameters (an object for the source of the event, and an object for event data).

Is an EventHandler a delegate?

The EventHandler delegate is a predefined delegate that specifically represents an event handler method for an event that does not generate data. If your event does generate data, you must use the generic EventHandler<TEventArgs> delegate class.

Are event handlers async?

NET events do not support async Task as a result type! Instead, you have to cast event handlers as async void if you want to run async code inside of an event handler method and to comply with the classic . NET event delegate signature.


1 Answers

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.

like image 198
Simon_Weaver Avatar answered Sep 21 '22 04:09

Simon_Weaver