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.
Events don’t mesh perfectly with
asyncandawait, as you’ve discovered.The way UIs handle
asyncevents is different than what you’re trying to do. The UI provides aSynchronizationContextto itsasyncevents, enabling them to resume on the UI thread. It does not ever "await" them.Best Solution (IMO)
I think the best option is to build your own
async-friendly pub/sub system, usingAsyncCountdownEventto know when all handlers have completed.Lesser Solution #1
async voidmethods do notify theirSynchronizationContextwhen they start and finish (by incrementing/decrementing the count of asynchronous operations). All UISynchronizationContexts ignore these notifications, but you could build a wrapper that tracks it and returns when the count is zero.Here’s an example, using
AsyncContextfrom my AsyncEx library:However, in this example the UI thread is not pumping messages while it’s in
Run.Lesser Solution #2
You could also make your own
SynchronizationContextbased on a nestedDispatcherframe that pops itself when the count of asynchronous operations reaches zero. However, you then introduce re-entrancy problems;DoEventswas left out of WPF on purpose.