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:
Imports System.Net
Imports System.Threading
Public Class Form1
Dim test As WebClient = New WebClient
Dim myToken As CancellationTokenSource
Dim myProgress As Progress(Of Integer)
Dim Counter As Integer = 0
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)
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
If Not myToken Is Nothing Then
myToken.Cancel()
End If
End Sub
Public Async Function startProgressBar(ct As CancellationTokenSource) As Task
Dim progressTarget As Action(Of Integer)
progressTarget = AddressOf ReportProgress
myProgress = New Progress(Of Integer)(progressTarget)
For I = 0 To 9
If ct.IsCancellationRequested = True Then
Exit For
End If
Dim myString As String = Await RetrieveString(myProgress)
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
Counter = Counter + 10
myProgress.Report(Counter)
End If
myString = Await test.DownloadStringTaskAsync("http://www.cnn.com")
Return myString
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.