It is quite common that in our application we use Modal Windows. Now more and more the asynchronous programming model is used pervasively. C# and VB.Net languages even provides a syntactic sugar for them using the async/await feature.
It is desirable that one could expect to
ShowDialog is a synchronous API. By definition, synchronous API can’t be
await ed; It doesn’t makes sense either trying to await a synchronous method.
However, you can make the synchronous method “awaitable” by wrapping it in a
Task — you do it using
Task.Factory.StartNew. That executes the given method asynchronously in ThreadPool thread making a synchronous operation asynchronous.
Sounds great right, So it is just a matter of wrapping
ShowDialog call in
Task.Run to make it asynchronous. Let’s do that.
Your synchronous code below
Alright, We’re done right? Yes, except that it won’t work –we’re done with it.
This could work for any other synchronous method, but not for anything which is related to UI. UI elements typically have “Thread affinity”. Which means they have to be dealt with the thread which it was created. Otherwise, You’ll get an
InvalidOperationException well known as “cross thread exception”.
In this case we could move the creation of window inside the
Task.Run(with some trick) and fix the violation of thread affinity requirement. But that will not yield you what you would expect. Window will not be shown as Modal window of the owner. etc.
It turns out that we can’t show the UI in worker thread(at least gracefully). It is evident that we must call the ShowDialog method from the user interface thread only. But the method being synchronous, preventing us from awaiting it. Are we back to the place where we started? No way around it?
Well, we can satisfy the requirement of calling the method in user interface thread itself and asynchronously also. Thanks to Dispatcher.BeginInvoke which makes it possible.
That is a different API which isn’t awaitable; nevertheless msft gifted us the TaskCompletionSource<T> type which could wrap anything in Task.
With just two more lines of code, it is possible
We could make this code as a reusable piece of code. Maybe a nice extension method as below
Then you could call it as
Note that the code is about Wpf, but it doesn’t matter much. You can pretty much do the same in any UI technology with very little change. For example, in winforms, you’d use
Form instead of
DialogResult instead of
Control.BeginInvoke method instead of
That’s about it. Hope you enjoyed the post. Let’s make everything asynchronous :). Have fun.