How to execute task in the wpf background while able to provide report and allow cancellation?
c#, task-parallel-library, wpf
Solution
I thought I answered your question here. If you need more sample code on how to do this using Task Parallel Library, with `CancellationTokenSource` and `IProgress<T>`, here it is:
Action _cancelWork;
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
this.StartButton.IsEnabled = false;
this.StopButton.IsEnabled = true;
try
{
var cancellationTokenSource = new CancellationTokenSource();
this._cancelWork = () =>
{
this.StopButton.IsEnabled = false;
cancellationTokenSource.Cancel();
};
var limit = 10;
var progressReport = new Progress<int>((i) =>
this.TextBox.Text = (100 * i / (limit-1)).ToString() + "%");
var token = cancellationTokenSource.Token;
await Task.Run(() =>
DoWork(limit, token, progressReport),
token);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
this.StartButton.IsEnabled = true;
this.StopButton.IsEnabled = false;
this._cancelWork = null;
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
this._cancelWork?.Invoke();
}
private int DoWork(
int limit,
CancellationToken token,
IProgress<int> progressReport)
{
var progress = 0;
for (int i = 0; i < limit; i++)
{
progressReport.Report(progress++);
Thread.Sleep(2000); // simulate a work item
token.ThrowIfCancellationRequested();
}
return limit;
}
Problem
I want to execute a long running task after clicking a wpf button. Here what I did. ``` private void Start(object sender, RoutedEventArgs e) { for (int i = 0; i < 10; i++) { Thread.Sleep(2000); // simulate task } } ``` Problem is, this will make wpf gui unresponsive. I also would like to allow cancellation and report progress every 1 second. I expand the code as below. ``` DispatcherTimer dispatcherTimer = new DispatcherTimer(); // get progress every second private int progress = 0; // for progress reporting private bool isCancelled = false; // cancellation private void Start(object sender, RoutedEventArgs e) { InitializeTimer(); // initiallize interval timer Start(10); // execute task } private void InitializeTimer() { dispatcherTimer.Tick += dispatcherTimer_Tick; dispatcherTimer.Interval = new TimeSpan(0,0,1); dispatcherTimer.Start(); } private void dispatcherTimer_Tick(object sender, EventArgs e) { Logger.Info("Current loop progress " + progress); // report progress } private void Cancel(object sender, RoutedEventArgs e) // cancel button { isCancelled = true; } private int Start(int limit) { isCancelled = true; progress = 0; for (int i = 0; i < limit; i++) { Thread.Sleep(2000); // simulate task progress = i; // for progress report if (isCancelled) // cancellation { break; } } return limit; } ``` My target platform is .NET 4.5. What is the recommended way to do this? Thanks.