My UI is asynchronous (Part 3)

Here we are, the last article about keeping the UI responsive with asynchrony. I have described two ways to implement it so far. In this last post we’ll two more.

Background Worker

The background worker is a component included in the .NET framework. It allows to execute a task outside of the main thread (in our case the UI thread). It sounds very technical but it’s actually very easy to use.

It’s an event driven component. You can subscribe to 3 useful events :

  • DoWork : It’s the main event, it contains the code to execute outside of the main thread. This event will be raised on the RunWorkerAsync method call. You can give it a parameter by using the first argument of type object. You’ll have to cast it to the type you want in order to use it in your code. Result datas can be stored in the Result property of the DoWorkEventArgs.

 

  • RunWorkerCompleted : This event is raised when the program exits the DoWork handler. You can retrieve the result of the previously executed by getting the Result property of the RunWorkerCompletedEventArgs. All the code in this part will be executed synchronously…so don’t forget it can freeze your UI.

 

  • ProgressChanged : This event is raised by the code executed in the DoWork handler. Thanks to it you can tell the user about the progress of the background task. Keeping the user aware helps him to wait for the task to complete. It’s always better to give real time feedback to your user. To set the ReportProgress mechanism just set  to true the WorkerReportsProgress property of your BackgroundWorker instance.

Here the complete example of the background worker

private void Asynchrony2()
        {
            BackgroundWorker worker = new BackgroundWorker();
            // Allow reports progress
            worker.WorkerReportsProgress = true;

            // Final result and UI update goes here
            worker.RunWorkerCompleted += (s, e) =>
            {
                this.lblResult.Text = e.Result.ToString();
            };

            // Long Running process goes here
            worker.DoWork += (s, e) =>
            {
                Thread.Sleep(250);
                (s as BackgroundWorker).ReportProgress(25);
                Thread.Sleep(250);
                (s as BackgroundWorker).ReportProgress(50);
                Thread.Sleep(250);
                (s as BackgroundWorker).ReportProgress(75);
                Thread.Sleep(250);
                (s as BackgroundWorker).ReportProgress(100);
                e.Result = LongRunningTask();
            };

            // Feedback to the user goes here
            worker.ProgressChanged += (s, e) =>
            {
                this.lblResult.Text = string.Format("Progress {0}%", e.ProgressPercentage);
            };

            worker.RunWorkerAsync();
        }

Async/Await pattern

If you’re lucky enough to work with the last version of the .NET framework you can even use the Async/Await pattern. It allows to simplify the implementation of asynchrony by using two keywords : async and await.

Look at our new long running task :

  async Task<string> LongRunningTaskAsync()
        {
            await Task.Delay(new TimeSpan(0, 0, 3));
            return DateTime.Now.ToString("hh:mm:ss");
        }

What this code does is just declaring a method which can be executed asynchronously. It sends back a task to the caller.

So, in order to call this method we’ll just have to modify the Click event of our button :

 private async void btnClick_Click(object sender, RoutedEventArgs e)
        {
            this.lblResult.Text = await LongRunningTaskAsync();
        }

Now our click event handler is marked as async, which means it can call an asynchronous method in its code. Using the keyword await we call the LongRunningTaskAsync method. .NET framework is now taken care of all the plumbing to make it asynchronous.

Conclusion

In conclusion I would say that BackgroundWorker should be favored for easy and straight long running tasks. If you need more control over your task scheduling, task continuations and complex algorithm I would rather go for the use of the Task factory.