Reputation:
I am writing a WPF program where I use EventToCommand to handle events in third party controls and wire them up to ICommands. My view model methods are all async and the problem I am having that the events are not waiting for the handlers to complete. Problem is a bit long to illustrate in WPF but I summarized it in the little console app below.
My general question is is there an implementation of EventToCommand that will allow me to use async handlers? Yes I did google it found nothing. The mvvm-light documentation here says nothing about async.
I am open to any work around that get the job done. Note in the code below I use the name "ButtonClick" which may lead you to ask why I don't just bind to the command.... that was a poor naming choice for the example sorry. In my WPF app I am handling events for which there is no command.
class Program
{
public event EventHandler ButtonClickEvent;
static void Main(string[] args)
{
Console.WriteLine("Program");
var p = new Program();
}
public Program()
{
ButtonClickEvent += ButtonClickEventHandler;
Console.WriteLine("Start fire event");
ButtonClickEvent(this, new EventArgs()); // In WPF app this event is handled by EventToCommand, which executes an ICommand
Console.WriteLine("End fire event");
Console.Write("Press any key...");
Console.ReadKey();
// Output
// Start fire event
// Start GetStringAsync
// End fire event <-- not waiting
// End GetStringAsync
}
// In WPF app this method is wired up to a DelegateCommand
public async void ButtonClickEventHandler(object sender, EventArgs e)
{
await DownloadStuffAsync();
}
public async Task DownloadStuffAsync()
{
Console.WriteLine("Start GetStringAsync");
string page = await new HttpClient().GetStringAsync("http://www.ibm.com");
Console.WriteLine("End GetStringAsync");
}
}
Upvotes: 0
Views: 384
Reputation: 456977
the problem I am having that the events are not waiting for the handlers to complete.
My general question is is there an implementation of EventToCommand that will allow me to use async handlers?
Well, look at the problem from the perspective of the third-party control. It's raising an event that looks something like this:
void ControlEvent(object sender, ControlEventArgs args);
When that delegate returns, it's done. The event handler signature returns void
, and all the arguments are data being passed from the control to the event handler. There's no "return communication". There's no way for the delegate to tell the third-party control "I'm not actually totally done yet; I'm just returning to free up the thread because I'm asynchronous; I have more code to execute later and then I'll really be done."
So, in short, what you want to do will probably require non-trivial changes to the third-party control. There are various hacks to try to force the asynchronous code to complete synchronously (as I describe in a recent MSDN article), but none of them work very well. A better solution as Sriram said is to change the third-party control to use Deferrals; I describe one possible implementation of Deferrals on my blog. However, this will be a non-trivial change for the third-party control, one which they may be unwilling to make.
Upvotes: 1
Reputation: 21
Sam event is nothing but a trigger which notifies that something has happened like Button click or mouse move. If you want to start doing something on that do that. But handle your async needs after that. Can you please explain the exact scenario what you need. so that I can help more?
Thanks Ankit
Upvotes: 0
Reputation: 3004
I tried the solution on the link giving by Sriram in my comment in WPF and it does work.
My ViewModel
public class ViewModel
{
public ICommand DoSomethingCommand { get; set; }
public ViewModel()
{
DoSomethingCommand = new RelayCommand(DoSomething);
}
private async void DoSomething()
{
await DownloadStuffAsync();
Debug.WriteLine("Button Call Completed");
}
public async Task DownloadStuffAsync()
{
Debug.WriteLine("Start GetStringAsync");
await Task.Run(() =>
{
Debug.WriteLine("Thread sleeping");
Thread.Sleep(5000);
}).ConfigureAwait(false);
string page = await new HttpClient().GetStringAsync("http://www.ibm.com").ConfigureAwait(false);
Debug.WriteLine("End GetStringAsync");
}
}
My View
<StackPanel>
<Button Content="Do Something" Margin="0,2,2,2" Command="{Binding DoSomethingCommand}"/>
</StackPanel>
and the Output on the debug window
Upvotes: 0
Reputation: 73492
EventHandlers doesn't support asynchronous methods. You need some Task
returning method as a workaround and await the Task
where you invoke the event.
You can use Deferral as explained here in Stephen Cleary's article on the subject. There are other solutions like overriding the SynchronizationContext
but using something like shown in the above article is better.
Upvotes: 1