Kickaha Kickaha - 9 days ago 5
C# Question

Running and interacting with a async task from a WPF gui

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 reports on progress, and I would like to incorporate another button that will stop the task at any time I choose.

I cannot figure the correct way to use async/await/task. I can;t include everything I've tried, but this is whatI have at the moment.

A WPF window class :

public partial class MainWindow : Window
{
readonly otherClass _burnBabyBurn = new OtherClass();
internal bool StopWorking = false;

//A button method to start the long running method
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
Task burnTheBaby = _burnBabyBurn.ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);

await burnTheBaby;
}

//A button Method to interrupt and stop the long running method
private void StopButton_Click(object sender, RoutedEventArgs e)
{
StopWorking = true;
}

//A method to allow the worker method to call back and update the gui
internal void UpdateWindow(string message)
{
TextBox1.Text = message;
}
}


And a class for the worker method:

class OtherClass
{
internal Task ExecuteLongProcedureAsync(MainWindow gui, int param1, int param2, int param3)
{
var tcs = new TaskCompletionSource<int>();

//Start doing work
gui.UpdateWindow("Work Started");

While(stillWorking)
{
//Mid procedure progress report
gui.UpdateWindow("Bath water n% thrown out");
if (gui.StopTraining) return tcs.Task;
}

//Exit message
gui.UpdateWindow("Done and Done");
return tcs.Task;
}
}


This runs, but the WPF function window is still blocked once the worker method starts.

I need to know how to arrange the async/await/task declarations to allow

A) the worker method to not block the gui window

B) let the worker method update the gui window

C) allow the gui window to stop interrupt and stop the worker method

Any help or pointers are much appreciated.

Answer

This is how you start a task using async and await:

private async void Button_Click_3(object sender, RoutedEventArgs e)
{
    var task = Task.Run(()=>
        ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3));
    await task;

    //or simply:
    await Task.Run(()=>
        ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3));

    //or if ExecuteLongProcedureAsync has a return value
    var returnValue = await Task.Run(()=>
        ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3));

    //in all three approaches this is where
    // the execution of ExecuteLongProcedureAsync ends
}

The method which you wish to execute asynchronously is something like this:

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(".");
    }

    gui.UpdateWindow("Done and Done");
} 

The last not the least, invoke the operation which involves a property from gui:

void UpdateWindow(string text)
{
    Dispatcher.Invoke(() =>
    {
        txt.Text += text;
    });
}