Simple .Net progress bar using async/await






4.60/5 (10 votes)
Using .Net's Async/Await Framework To Create ProgressBar
Introduction
For the beginning C#/VB.Net developer, one of the most frustrating (yet seemingly simple) things to add to a project is a progress bar that updates while the program does something in the background (like load files or download web pages). To accomplish this within previous versions of .Net, you'd be prompted to use threads or a background worker. Fortunately, with the async/await framework provided in .Net 4.5 and Visual Studio 2012, implementing this type of functionality is relatively easy.
In the following example, I will show you how to implement a progress bar that updates in real time while we download information from the Internet and gives the user the option to cancel the process, if so desired.
Background
If you do a Google search for how to implement a progress bar that updates while your .Net program does something in the background, you will most likely find older code examples that use threads or the background worker. While these examples are helpful, I found that those processes are still pretty confusing.
With the advent of .Net 4.5, things get much easier. Using the async/await framework, it is relatively straightforward to structure your functions so that you can easily execute processes "in the background" while your GUI remains responsive. You can also make it easy for the user to cancel a background process that has been initiated but not yet completed.
The key here is the async/await framework. There is a lot of good information already available online about how this works, and I recommend you read it at some point.
From my limited experience, however, the gist of this is as follows:
- The key point is to define your functions with the async keyword (see example) and each function that includes the async keyword must include, somewhere in its code, the await keyword as well.
- As you'll see in this example, this chain of async and await goes all the wait down until we use the .Net command .DownloadStringTaskAsync (string) which retrieves a web page and returns it in string format. Note that we are using the .DownloadStringTaskAsync command simply as an example of an asynchronous function. You can use whatever asynchronous function your program requires.
- To track the progress of your background function, you have to create and pass a Progress object. This object is used by the asynchronous function to report progress back to the function that called it. Unlike the background worker, you can report progress in terms of data types other than just integers (although that's what we use here).
- To give your user the ability to cancel the process as it is ongoing, you need to create a CancellationTokenSource (and, in turn, manage the .token that is part of this source). This is not as hard as it sounds.
Don't get discouraged by all of these new terms. Check out the source code that follows and see if you can follow along in terms of what the program is doing. It's not very long, which I guess is testament to how easy it is to use this framework.
Using the code
To use this code, create a new Windows Form called Form1 and insert the following items:
- A label called 'Label1'
- A progress bar called 'ProgressBar1'
- A button called 'btnStart' and label it 'Start'
- A button called 'btnStop' and label it 'Stop'
Your form should look something like this:
Next, double click on the form and replace all of the code in it with the following code:
' Simple progress bar example with cancellation and label updating
' Using the ASYNC and AWAIT keywords available through .Net 4.5
' Helpful sources include:
' - http://msdn.microsoft.com/en-us/library/hh191443.aspx
' - http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx
Imports System.Net
Imports System.Threading
Public Class Form1
Dim test As WebClient = New WebClient ' This is specific to the type of async function we're doing. Yours may be different.
Dim myToken As CancellationTokenSource ' This is used to notify the async function that we want to cancel it.
Dim myProgress As Progress(Of Integer) ' We use this as the vehicle for reporting progress back to our GUI thread
Dim Counter As Integer = 0 ' This is a variable that we use to track our progress (specific to this example)
Private Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
ProgressBar1.Value = 0
Counter = 0
myToken = New CancellationTokenSource
Await startProgressBar(myToken) ' Note that the signature of our startprogressbar method includes the cancellationtokensource
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
If Not myToken Is Nothing Then
myToken.Cancel() ' We tell our token to cancel
End If
End Sub
Public Async Function startProgressBar(ct As CancellationTokenSource) As Task
Dim progressTarget As Action(Of Integer) ' We have to create an event handler for the function we use to report progress
progressTarget = AddressOf ReportProgress ' We assign the handler to our specific function (see below)
myProgress = New Progress(Of Integer)(progressTarget) ' When we initialize our progress reporter, we pass our handler to it
For I = 0 To 9
If ct.IsCancellationRequested = True Then
Exit For ' We then break out of the loop
End If
Dim myString As String = Await RetrieveString(myProgress) ' We don't do anything with this string, just so we can run our async function
Next
End Function
Public Async Function RetrieveString(ByVal myProgress As IProgress(Of Integer)) As Task(Of String)
Dim myString As String = ""
If Not myProgress Is Nothing Then 'If we have a valid progress object, report our progress using it
Counter = Counter + 10
myProgress.Report(Counter)
End If
myString = Await test.DownloadStringTaskAsync("http://www.cnn.com") ' This is a throwaway function - just something that uses async
Return myString ' We are really not doing anything with the results of this function, just returning it
End Function
Private Sub ReportProgress(ByVal myInt As Integer)
Label1.Text = "Step Number: " & myInt.ToString
ProgressBar1.Value = myInt
End Sub
End Class
As you will see, this simple program downloads the contents of the CNN.com homepage into a string 10 times in a row. Note that we're using the .DownloadStringTaskAsync function simply as an example - you can do whatever you want, as long as it is a genuine asynchronous function.
Points of Interest
Feel free to share any comments, bugs, suggestions and etc. below. I'm sure that I have a lot more to learn about using this framework.
More information on using .Net's asynchronous framework can be found at:
http://msdn.microsoft.com/en-us/library/hh191443.aspx
History
Keep a running update of any changes or improvements you've made here.