I have a WPF GUI, where I want to press a button to start a long task without freezing the window for the duration of the task. While the task is running I would like to get
This is a simplified version of the most popular answer here by Bijan. I simplified Bijan's answer to help me think through the problem using the nice formatting provided by Stack Overflow.
By carefully reading and editing Bijan's post I finally understood: How to wait for async method to complete?
In my case the chosen answer for that other post is what ultimately led me to solve my problem:
"Avoid async void. Have your methods return Task instead of void. Then you can await them."
My simplified version of Bijan's (excellent) answer follows:
1) This starts a task using async and await:
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
// if ExecuteLongProcedureAsync has a return value
var returnValue = await Task.Run(()=>
ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3));
}
2) This is the method to execute asynchronously:
bool stillWorking = true;
internal void ExecuteLongProcedureAsync(MainWindow gui, int param1, int param2, int param3)
{
//Start doing work
gui.UpdateWindow("Work Started");
while (stillWorking)
{
//put a dot in the window showing the progress
gui.UpdateWindow(".");
//the following line blocks main thread unless
//ExecuteLongProcedureAsync is called with await keyword
System.Threading.Thread.Sleep(50);
}
gui.UpdateWindow("Done and Done");
}
3) Invoke the operation which involves a property from gui:
void UpdateWindow(string text)
{
//safe call
Dispatcher.Invoke(() =>
{
txt.Text += text;
});
}
Or,
void UpdateWindow(string text)
{
//simply
txt.Text += text;
}
Closing comments) In most cases you have two methods.
First method (Button_Click_3) calls the second method and has the async modifier which tells the compiler to enable threading for that method.
Thread.Sleep in an async method blocks the main thread. but awaiting a task does not.await statements until task is finished.await outside an async methodSecond method (ExecuteLongProcedureAsync) is wrapped within a task and returns a generic Task object which can be instructed to be processed asynchronously by adding await before it.
Liero brought up an important issue. When you are Binding an element to a ViewModel property, the property changed callback is executed in UI thread. So there is no need to use Dispatcher.Invoke. Value changes fired by INotifyPropertyChanged are automatically marshalled back onto the dispatcher.