Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute task in the wpf background while able to provide report and allow cancellation?

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.

like image 728
Syaiful Nizam Yahya Avatar asked Jan 25 '14 07:01

Syaiful Nizam Yahya


People also ask

How to run the backgroundworker process in WPF?

Here we show an example of the BackgroundWorker class in WPF. Step 2: In order to run the BackgroundWorker process we use the RunWorkerAsync method of the BackgroundWorker class. This mehod is used to start the execution of the background process by the raising of the DoWork event.

What is the use of backgroundworker class?

The BackgroundWorker class is used to run time-consuming tasks in the background; it leaves the user interface responsive. Sometimes we need to perform some time consuming tasks such as downloads etc. Execution of these tasks consumes a large amount of time.

How do I run a time-consuming operation in the background?

The following code example shows how to run a time-consuming operation in the background. The form has Start and Cancel buttons. Click the Start button to run an asynchronous operation. Click the Cancel button to stop a running asynchronous operation. The outcome of each operation is displayed in a MessageBox.

Why can’t I call the view model from my background task?

Since the service class methods are running on a background thread, they can’t call a View Model method (which runs on the main thread) directly. We saw this above, in the last ContinueWith () call. In this case, we use the Dispatcher.Invoke () approach to call the View Model from our background task:


1 Answers

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;
}
like image 180
noseratio Avatar answered Sep 28 '22 17:09

noseratio