Hands-On Lab
Asynchronous Programming in the .NET Framework 4.5
Lab version: 1.1.0.0
Last updated: 1/2/2013
CONTENTS
OVERVIEW ............................................................................................................................................. 3
EXERCISE 1: ASYNC METHODS .......................................................................................................... 5 Task 1 – Creating the Application in the Old Way ............................................................................. 5
Task 2 – Using Asynchronous Code ................................................................................................ 11
EXERCISE 2: CONCURRENT DOWNLOAD ........................................................................................ 13 Task 1 – Adding Concurrent Download in a Separate Method ........................................................ 13
Task 2 – Verification ....................................................................................................................... 20
EXERCISE 3: POLLING AND CANCELLATION ................................................................................... 21 Task 1 – Adding the Cancellation Feature ....................................................................................... 21
Task 2 – Polling the Linked Pages to Detect Changes ...................................................................... 27
EXERCISE 4: ASYNC LIBRARY REFACTORING ................................................................................ 34 Task 1 – Creating an Asynchronous Class Library ............................................................................ 34
Task 2 – Refactoring the Project ..................................................................................................... 35
APPENDIX: USING CODE SNIPPETS.................................................................................................. 40
To give feedback please write to [email protected].
Copyright © 2013 by Microsoft Corporation. All rights reserved.
Overview
If your user interface is unresponsive or your server does not scale, chances are you could improve
matters with asynchronous code. Microsoft .NET Framework 4.5 introduces new language features in C#
and Visual Basic to make asynchronous programming in .NET far simpler. This new foundation makes
asynchronous programming very similar to synchronous (i.e., ordinary) programming.
In this Hands-On Lab you will learn how to implement asynchronous programming with the new
features introduced by Microsoft .NET Framework 4.5.
Introduction to Asynchronous Operations
Asynchronous operations represent potentially slow work, they but return almost immediately, enabling
the initiating thread to get on with other work while the operation continues to run concurrently.
The language-level support for asynchronous operation requires methods to implement a particular
pattern, called the .NET Task-Based Asynchronous Pattern (TAP). An asynchronous method must return
without waiting for the operation it starts to complete. That would be true for any asynchronous
pattern, but to conform to the TAP, a method must also return an object that represents the ongoing
operation and enables you to discover its eventual outcome. C# and VB require this object to offer
certain functionality, and although do not require it to be of any particular type, it is very common
choice to return a Task object.
Tasks have been available since .NET 4.0, but invoking and then waiting asynchronously for a task-
returning method to complete used to require potentially complex callback-based code. But with .NET
4.5, it has been simplified to a single expression: “await MyAsyncMethod()”. Before .NET 4.5, the
callbacks required to handle the asynchronous work efficiently could make code hard to read,
particularly if you needed to use loops or other flow control constructs. With .NET 4.5’s new
asynchronous model, you can manage control flow in exactly the same way as you would with ordinary
synchronous code, because you do not need to write explicit callbacks. The compiler takes care of
creating and registering these for you, so you can still enjoy all the performance benefits of
asynchronous code, but you no longer have to pay the price of complexity.
Throughout this lab, you will become familiar with the implementation of simple asynchronous logic to
prevent blocking the UI when performing remote calls or other slow operations.
Note: Asynchronous techniques are not limited to UI code—they are important for getting the most
out of server-side code. The techniques are the same in either case though, so you can use the
approach shown in this lab on servers too. You can find more references on .NET Asynchronous
programming and the Task-Based Asynchronous Pattern at http://msdn.microsoft.com/async.
Objectives
In this hands-on lab, you will learn how to:
Convert synchronous code into asynchronous code
Perform slow tasks without blocking the UI
Run multiple asynchronous operations concurrently
Implement cancellation and polling features
Create a DLL library that exposes asynchronous methods
Prerequisites
Microsoft Visual Studio 2013
Windows PowerShell (for setup scripts – already installed on Windows 8, Windows Server 2012,
Windows 7 and Windows Server 2008 R2)
Code Snippets Setup
Throughout the lab document, you will be instructed to insert code blocks. For your convenience, most
of that code is provided as Visual Studio Code Snippets, which you can use from within Visual Studio to
avoid having to add it manually.
You can optionally install the provided code snippets:
1. Run Visual Studio as an Administrator.
2. Select Tools|Code Snippets Manager.
3. Select Visual C# from the Languages dropdown list.
4. Click Add.
5. Browse to the lab’s Source folder located on your desktop’s folder TechEd
2014\Asynchronous Programming in the .NET Framework.
6. Select the Async Lab Snippets folder.
7. Click Select Folder.
Formatted: Font: Bold
8. Click OK to close the Code Snippets Manager.
Exercises
This hands-on lab includes the following exercises:
1. Async Methods
2. Concurrent Download
3. Polling and Cancellation
4. Async Library Refactoring
Note: The exercises form a sequence, where each builds on the previous one. However, if you want to
tackle them out of order, you can. Exercises 2-4 are accompanied by a starting solution that you can
use if you did not complete the previous exercise. You may also find these useful if you choose not to
follow the instructions precisely. These starter solutions are in the Begin folder for each exercise.
(There is no Begin folder for Exercise 1 because you will start by creating a fresh project.) Please be
aware that the code snippets that you will add during an exercise are missing from these starting
solutions and that they will not necessarily work until you complete the exercise.
Inside the folder for each exercise, you will also find an End folder containing a Visual Studio solution
with the code that results from completing the steps in the corresponding exercise. You can use these
solutions as guidance if you need additional help as you work through this hands-on lab.
Exercise 1: Async Methods
In this exercise, you will create a very simple application that will download the HTML content of a web
page and, using regular expressions, search through that content looking for any link. Then, you will
display those links in a ListBox. To begin with, you will build the application using synchronous code and
examine the issues with this implementation. Then, you will modify your code to work asynchronously,
addressing the issues with the first version of the application.
Task 1 – Creating the Application in the Old Way
In this task, you will create a WPF application to download a web page and find any links in the HTML.
You will write the code in the old way by using synchronous calls, and finally, run the application to see
the disadvantages of this way of programming.
1. Open Visual Studio 2013 and create a new WPF Application project with the name AsyncLab.
Choose either C# or Visual Basic, according to your preference.
2. In the MainWindow.xaml designer, resize your application’s window to 640x480.
3. Drop a TextBox on the left to display the HTML and name it “textBox1”; drop and a ListBox on
the right to display the found links and name it “listBox1”.
4. Drop a button at the bottom and set its content to “Start” and its name to “Start”. Your XAML
should now look something like this:
XAML
<Window x:Class="AsyncLab.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="640" Height="480">
<Grid>
<TextBox Height="379" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="textBox1" VerticalAlignment="Top" Width="294" />
<ListBox Height="379" HorizontalAlignment="Right" Margin="0,12,12,0"
Name="listBox1" VerticalAlignment="Top" Width="294" />
<Button Content="Start" Height="23" HorizontalAlignment="Left"
Margin="272,406,0,0" Name="Start" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
Note: If you decide to copy and paste the XAML from this document rather than creating it
interactively in Visual Studio’s XAML designer, make sure you only copy the highlighted lines.
5. Double-click the Start button to create a Click event handler.
6. Add code to disable the Start button while the work is in progress, and enable it once the work
is completed.
(Code Snippet – Async Lab - Ex01 - Disable Enable Start - CS)
C#
private void Start_Click(object sender, RoutedEventArgs e)
{
this.Start.IsEnabled = false;
this.Start.IsEnabled = true;
}
(Code Snippet – Async Lab - Ex01 - Disable Enable Start - VB)
Visual Basic
Private Sub Start_Click(sender As Object, e As RoutedEventArgs) Handles
Start.Click
Me.Start.IsEnabled = False
Me.Start.IsEnabled = True
End Sub
7. At the beginning of your event handler, declare an initialize a variable holding a list of strings to
store the URIs.
(Code Snippet – Async Lab - Ex01 - URIs List - CS)
C#
private void Start_Click(object sender, RoutedEventArgs e)
{
List<string> uris = new List<string>();
this.Start.IsEnabled = false;
(Code Snippet – Async Lab - Ex01 - URIs List - VB)
Visual Basic
Private Sub Start_Click(sender As System.Object, e As
System.Windows.RoutedEventArgs) Handles Start.Click
Dim uris As New List(Of String)()
Me.Start.IsEnabled = False
8. In between the code that disables and enables the Start button, insert a try-catch block. (This
will contain the main functionality of the application.) In the catch block, implement an
exception handler to display a message box with the description of any error that occurs.
(Code Snippet – Async Lab - Ex01 - TryCatch - CS)
C#
List<string> uris = new List<string>();
this.Start.IsEnabled = false;
try
{
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
this.Start.IsEnabled = true;
(Code Snippet – Async Lab - Ex01 - TryCatch - VB)
Visual Basic
Dim uris As New List(Of String)()
Me.Start.IsEnabled = False
Try
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
Me.Start.IsEnabled = True
9. Add a project reference to System.Net.Http. (If the Reference Manager dialog lists multiple
versions, choose 4.0.0.0.)
10. Import the following namespaces:
(Code Snippet – Async Lab - Ex01 - Namespaces - CS)
C#
using System.Net.Http;
using System.Text.RegularExpressions;
(Code Snippet – Async Lab - Ex01 - Namespaces - VB)
Visual Basic
Imports System.Net.Http
Imports System.Text.RegularExpressions
11. Insert the following code to download the HTML content from a specific URL, and display the
result in the textbox.
(Code Snippet – Async Lab - Ex01 - Download Page - CS)
C#
try
{
var response = new HttpClient()
.GetAsync("http://msdn.microsoft.com").Result;
string result = response.EnsureSuccessStatusCode().Content
.ReadAsStringAsync().Result;
this.textBox1.Text = result;
}
catch (Exception)
{
MessageBox.Show(ex.ToString());
}
(Code Snippet – Async Lab - Ex01 - Download Page - VB)
Visual Basic
Try
Dim response = New HttpClient().GetAsync("http://msdn.microsoft.com").Result
Dim result As String =
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().Result
Me.textBox1.Text = result
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
Note: The HttpClient class only support asynchronous operation—its GetAsync and
ReadAsStringAsync methods both return tasks, and there are no corresponding Get or
ReadAsString methods. However, this code uses these tasks’ Result property, which
effectively converts this into synchronous operation. (If you read the Result of an incomplete
task, it blocks your thread until the task completes, so that it can return the result.) This means
that although we are using asynchronous methods (because that’s all HttpClient offers) we’re
using them in a synchronous way.
12. Use a regular expression to search for links, store them in the URIs list, and then, bind the URIs
list to the ListBox.
(Code Snippet – Async Lab - Ex01 - Search Links - CS)
C#
this.textBox1.Text = result;
MatchCollection mc = Regex.Matches(result,
"href\\s*=\\s*(?:\"(?<1>http://[^\"]*)\")", RegexOptions.IgnoreCase);
foreach (Match m in mc)
{
uris.Add(m.Groups[1].Value);
}
this.listBox1.ItemsSource = uris;
}
(Code Snippet – Async Lab - Ex01 - Search Links - VB)
Visual Basic
Me.textBox1.Text = result
Dim mc As MatchCollection = Regex.Matches(result,
"href\s*=\s*(?:\""(?<1>http://[^""]*)\"")", RegexOptions.IgnoreCase)
For Each m As Match In mc
uris.Add(m.Groups(1).Value)
Next
Me.listBox1.ItemsSource = uris
Catch ex As Exception
13. Press F5 to start debugging. When the application starts, click the Start button.
Figure 1
Running application
Note: Notice that for as long as it takes to download the content, the window is unresponsive.
Also, the button does not change its appearance to indicate that it is disabled. Both of these
problems are caused by the fact that this code blocks the thread while the application is
downloading the web page.
Task 2 – Using Asynchronous Code
In this task, you will enhance the application you wrote in the previous task to use asynchronous
techniques. To do this, you will implement an asynchronous method to download the page in the
background.
1. Add the async keyword in the Start_Click function.
C#
private async void Start_Click(object sender, RoutedEventArgs e)
{
Visual Basic
Private Async Sub Start_Click(sender As Object, e As RoutedEventArgs) Handles
Start.Click
Note: The async keyword (or Async in VB) is new in .NET Framework 4.5. It tells the compiler
that the current method will contain asynchronous code.
Of course, right now your method does not yet contain any asynchronous code, so you may
see a compiler warning at this point, telling you that the async keyword only makes sense in a
method that uses await. The next two steps will fix this.
2. Remove the use of the Result property after the HttpClient GetAsync method call, and also
after the ReadAsStringAync call on the following line.
C#
try
{
var response = new HttpClient().GetAsync("http://msdn.microsoft.com");
string result =
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
this.textBox1.Text = result;
...
Visual Basic
Try
Dim response = New HttpClient().GetAsync("http://msdn.microsoft.com")
Dim result As String =
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()
Me.textBox1.Text = result
...
Note: As before, we’re using the GetAsync and ReadAsStringAsync methods, but we’re no
longer reading the Result property, so we’re now going to be using HttpClient as intended:
asynchronously. We are not quite there yet, though, so you will get an error if you attempt to
compile the code right now. The next step will fix that.
3. Add the await keyword in front of the expression that calls GetAsync(), and also in front of the
one that calls ReadAsStringAsync().
(Code Snippet – Async Lab - Ex01 - Download Page Async - CS)
C#
try
{
var response = await new HttpClient().GetAsync("http://msdn.microsoft.com");
string result = await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
this.textBox1.Text = result;
...
(Code Snippet – Async Lab - Ex01 - Download Page Async - VB)
Visual Basic
Try
Dim response = Await New HttpClient().GetAsync("http://msdn.microsoft.com")
Dim result As String = Await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()
Me.textBox1.Text = result
...
Note: When you add the await keyword, you are telling the compiler that the task must
complete before the method can continue. Although it looks like it will simply block the thread
while waiting for this to happen (just like when we read the Result property earlier), under the
covers the compiler arranges for the rest of the code to execute as a callback after the awaited
operation completes. (Your click handler method will return in the meantime.) You do not
need to change your try-catch block in order to make this work. The try/catch block will still
catch any exceptions that happen, whether they occur in asynchronous operations for which
your code awaits, or in normal non-asynchronous parts of the code. This requires the compiler
to generate relatively complex code under the covers, code that you would have had to have
written yourself with earlier versions of .NET.
4. Press F5 to run the application and click the Start button. Notice that the button now changes
visibly to a disabled state for as long as the download takes to complete, and that the
application remains responsive while that happens. With these modifications, your application
never blocks the UI thread, because the download work now happens in the background; the
click handler allows the UI thread to return to its message loop while that work is in progress.
Exercise 2: Concurrent Download
In this exercise, you will enhance the solution from Exercise 1 to download the content for all of the links
it discovered, while remaining responsive to user input.
Task 1 – Adding Concurrent Download in a Separate Method
1. Open Visual Studio 2013 and load AsyncLab-Ex2-Begin.sln solution located on your desktop in
the folder STechEd 2014\Asynchronous Programming in the .NET
Framework\Source\[CS|VB]\Ex2-Concurrency\Begin folder of this lab. Alternatively, if you
completed Exercise 1, you can continue working with the solution you created in that part.
2. Open MainWindow.xaml.cs or MainWindow.xaml.vb code-behind and import the
System.Collection.ObjectModel namespace. Click on the Ok button on the security warning.
C#
...
using System.Collections.ObjectModel;
Visual Basic
...
Imports System.Collections.ObjectModel
3. In Start_Click event handler, change the uris local variable to use the generic
ObservableCollection type instead the generic List type.
C#
private async void Start_Click(object sender, RoutedEventArgs e)
{
List<string> uris = new List<string>();
ObservableCollection<string> uris = new ObservableCollection<string>();
this.Start.IsEnabled = false;
try
Formatted: Font: Bold
{
...
Visual Basic
Private Async Sub Start_Click(sender As System.Object, e As
System.Windows.RoutedEventArgs) Handles Start.Click
Dim uris As New List(Of String)()
Dim uris As New ObservableCollection(Of String)()
Me.Start.IsEnabled = False
4. To improve the application responsiveness, you will now use a Task to run the code that finds
links in the document. This will move that work to a thread pool thread, ensuring that the UI
thread remains responsive even if that code takes a long time to run.
Enclose the filter and list manipulation logic into a lambda expression, which will run on
background thread when calling Task.Run. This Start_Click method needs to wait for this task to
complete, because it needs to put the resulting data into a ListBox, but it will perform that wait
asynchronously, using the await keyword.
(Code Snippet – Async Lab - Ex02 - AwaitBlock - CS)
C#
private async void Start_Click(object sender, RoutedEventArgs e)
{
...
try
{
...
this.textBox1.Text = result;
await Task.Run(() =>
{
MatchCollection mc = Regex.Matches(result,
"href\\s*=\\s*(?:\"(?<1>http://[^\"]*)\")", RegexOptions.IgnoreCase);
foreach (Match m in mc)
{
uris.Add(m.Groups[1].Value);
}
});
(Code Snippet – Async Lab - Ex02 - AwaitBlock - VB)
Visual Basic
Private Async Sub Start_Click(sender As System.Object, e As
System.Windows.RoutedEventArgs) Handles Start.Click
...
Try
...
Me.textBox1.Text = result
Await Task.Run(Sub()
Dim mc As MatchCollection = Regex.Matches(result,
"href\s*=\s*(?:\""(?<1>http://[^""]*)\"")", RegexOptions.IgnoreCase)
For Each m As Match In mc
uris.Add(m.Groups(1).Value)
Next
End Sub)
Note: By using Task.Run, you are explicitly running your logic on a thread from the thread
pool. However, the await ensures that once this task completes, any further work will resume
back on the UI thread. That is important here, because the Click handler method goes on to
update a ListBox, and it is illegal to do that from any thread other than the UI thread.
5. Create a new class in the root of AsyncLab project and name it LinkInfo. This class will contain
information about the links of the page. Replace the LinkInfo class with the following code.
(Code Snippet – Async Lab - Ex02 - LinkInfo - CS)
C#
namespace AsyncLab
{
public class LinkInfo
{
public string Title { get; set; }
public string Html { get; set; }
public int Length { get; set; }
}
}
(Code Snippet – Async Lab - Ex02 - LinkInfo - VB)
Visual Basic
Public Class LinkInfo
Public Property Title() As String
Public Property Html() As String
Public Property Length As Integer
End Class
6. Create a new method to download a page in the MainWindow.xaml.cs or
MainWindow.xaml.vb code-behind class. Name this method DownloadItemAsync and declare
it with the async prefix. This method will return a LinkInfo with an empty string should the HTTP
GET operation fail. The method GetTtitle wil be implemented in the next step.
(Code Snippet – Async Lab - Ex02 - DownloadItemAsync - CS)
C#
private static async Task<LinkInfo> DownloadItemAsync(Uri itemUri)
{
string item;
try
{
HttpClient httpClient = new HttpClient();
httpClient.MaxResponseContentBufferSize = 1000000;
var response = await httpClient.GetAsync(itemUri);
item = await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
}
catch
{
item = string.Empty;
}
LinkInfo linkInfo = new LinkInfo { Length = item.Length, Title =
GetTitle(item), Html = item };
return linkInfo;
}
(Code Snippet – Async Lab - Ex02 - DownloadItemAsync - VB)
Visual Basic
Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri) As
Task(Of LinkInfo)
Dim item As String
Try
Dim httpClient As New HttpClient
httpClient.MaxResponseContentBufferSize = 1000000
Dim response = Await httpClient.GetAsync(itemUri)
item = Await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()
Catch
item = String.Empty
End Try
Formatted: Font: Bold
Dim linkInfo As LinkInfo = New LinkInfo() With {.Length = item.Length, .Html
= item, .Title = GetTitle(item)}
Return linkInfo
End Function
Note: Notice we are setting a relatively large HTTP client buffer size. This is necessary because
default value is about 65Kb, and many web pages are larger than this, so downloading them
would generate an application error if we had not increased the buffer size.
7. Create a GetTitle method after the DownloadItemAsync method to return the text located
inside the title tag.
(Code Snippet – Async Lab - Ex02 - GetTitle - CS)
C#
private static string GetTitle(string html)
{
if (html.Length == 0)
{
return "Not Found";
}
Match m = Regex.Match(html, @"(?<=<title.*>)([\s\S]*)(?=</title>)",
RegexOptions.IgnoreCase);
return m.Value;
}
(Code Snippet – Async Lab - Ex02 - GetTitle - VB)
Visual Basic
Private Shared Function GetTitle(ByVal html As String) As String
If (html.Length.Equals(0)) Then
Return "Not Found"
End If
Dim m As Match = Regex.Match(Html, "(?<=<title.*>)([\s\S]*)(?=</title>)",
RegexOptions.IgnoreCase)
Return m.Value
End Function
8. Modify the Start_Click event handler logic to use the DownloadItemAsync method and
populate the ListBox with the retrieved items.
(Code Snippet – Async Lab - Ex02 - DownloadCompleted - CS)
C#
private async void Start_Click(object sender, RoutedEventArgs e)
{
...
try
{
...
await Task.Run(() =>
{
MatchCollection mc = Regex.Matches(result,
"href\\s*=\\s*(?:\"(?<1>http://[^\"]*)\")", RegexOptions.IgnoreCase);
foreach (Match m in mc)
{
uris.Add(m.Groups[1].Value);
}
});
this.listBox1.ItemsSource = await Task.WhenAll(from uri in uris select
DownloadItemAsync(new Uri(uri)));
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
(Code Snippet – Async Lab - Ex02 - DownloadCompleted - VB)
Visual Basic
Private Async Sub Start_Click(sender As System.Object, e As
System.Windows.RoutedEventArgs) Handles Start.Click
...
Try
...
Await Task.Run(Sub()
Dim mc As MatchCollection = Regex.Matches(result,
"href\s*=\s*(?:\""(?<1>http://[^""]*)\"")", RegexOptions.IgnoreCase)
For Each m As Match In mc
uris.Add(m.Groups(1).Value)
Next
End Sub)
Me.listBox1.ItemsSource = Await Task.WhenAll( _
From uri In uris _
Select DownloadItemAsync(New Uri(uri)))
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
...
End Sub
Note:
- The Task.WhenAll method takes a collection of tasks as input, and returns a new task that
completes only when all of the tasks you passed in have completed. The result of the task is an
array containing each of the results from the constituent tasks. This example uses the await
keyword on the task returned by Task.WhenAll, so the effect is that the code will perform an
asynchronous wait for that task (using callbacks under the covers), and the statement will only
complete when every task has finished, at which point it writes the resulting array into the list
box’s ItemsSource property.
- There is also a Task.WhenAny method (not used here). That returns a task that completes as
soon as at least one of the constituent tasks completes.
9. Open MainWindow.xaml XAML view and insert the following code before the Grid. This is a
template that you will use to show the link title and the length of each linked page.
XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="480" Width="640">
<Window.Resources>
<DataTemplate x:Key="DataTemplateItem">
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat=" {0} - {1} ">
<Binding Path="Length"/>
<Binding Path="Title"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
...
10. Modify the ListBox to use the data template from the previous step.
XAML
<Grid>
<TextBox Height="379" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="textBox1" VerticalAlignment="Top" Width="294" />
<ListBox Height="379" HorizontalAlignment="Right" Margin="0,12,12,0"
Name="listBox1" VerticalAlignment="Top" Width="294"
ItemTemplate="{DynamicResource DataTemplateItem}"/>
<Button Content="Start" Height="23" HorizontalAlignment="Left"
Margin="272,406,0,0" Name="Start" VerticalAlignment="Top" Width="75"
Click="Start_Click" />
</Grid>
Task 2 – Verification
1. Press F5 to run the application.
2. Click the Start button to begin downloading the linked pages. The left-hand pane should show
the HTML for the initial web page reasonably quickly. The list box on the right will take longer to
populate. (Even though the code fetches multiple pages concurrently, it will still have to
download over 100 pages, so depending on how fast your Internet connection is, this could take
a while.) Note that the application is still responsive while the downloads execute in the
background.
3. After all of the downloads complete, the application will show the list of pages with their length
and title.
Figure 2
Running the application
Exercise 3: Polling and Cancellation
In this exercise, you will add additional features to the application. First, you will introduce the ability to
cancel the work that is running in the background. Then, you will add the ability to see when each of the
pages the main page refers to changes. (You will do this by polling – downloading each page repeatedly
to check for changes.) If the web site offers different content than the last time, you will notify the user
by changing the link color in the ListBox.
Task 1 – Adding the Cancellation Feature
In this task, you will implement the cancellation feature to terminate downloads in progress.
1. Open Visual Studio 2013 and load the AsyncLab-Ex3-Begin.sln solution located on your desktop
in the folder TechEd 2014\Asynchronous Programming in the .NET
Framework\Source\[CS|VB]\Ex2-Concurrency\Begin.solution located in the
Source\[CS|VB]\Ex3-PollingAndCancellation\Begin folder of this lab. Alternatively, you can
continue working with the solution you created in Exercise 2, if you chose to follow that to the
end.
2. Open MainWindow.xaml.cs or MainWindow.xaml.vb and import the System.Threading
namespace.
C#
using System.Threading;
Visual Basic
Imports System.Threading
3. Define a class field to hold the cancellation token.
(Code Snippet – Async Lab - Ex03 - Cancellation Token - CS)
C#
public partial class MainWindow : Window
{
private CancellationTokenSource cancellationToken;
(Code Snippet – Async Lab - Ex03 - Cancellation Token - VB)
Visual Basic
Class MainWindow
Private cancellationToken As CancellationTokenSource
4. Modify the DownloadItemAsync method to receive a CancellationToken parameter.
C#
private static async Task<LinkInfo> DownloadItemAsync(Uri itemUri,
CancellationToken cancellationToken)
{
Visual Basic
Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri, ByVal
cancellationToken As CancellationToken) As Task(Of LinkInfo)
5. Create a new instance of the cancellation token inside the Start_Click event handler.
(Code Snippet – Async Lab - Ex03 - Cancellation Token Instance - CS)
C#
private async void Start_Click(object sender, RoutedEventArgs e)
{
this.cancellationToken = new CancellationTokenSource();
ObservableCollection<string> uris = new ObservableCollection<string>();
(Code Snippet – Async Lab - Ex03 - Cancellation Token Instance - VB)
Visual Basic
Private Async Sub Start_Click(sender As System.Object, e As
System.Windows.RoutedEventArgs) Handles Start.Click
Me.cancellationToken = New CancellationTokenSource()
Dim uris As New ObservableCollection(Of String)()
6. In the Start_Click method, modify the HttpClient call to include a cancellation token. To do this,
replace the GetAsync call with two lines, the first of which creates a request message object,
and the second of which uses the SendAsync method, which supports cancellation tokens.
(Code Snippet – Async Lab - Ex03 - StartClick with CancellationToken - CS)
C#
private async void Start_Click(object sender, RoutedEventArgs e)
{
this.cancellationToken = new CancellationTokenSource();
ObservableCollection<string> uris = new ObservableCollection<string>();
this.Start.IsEnabled = false;
try
{
var message = new HttpRequestMessage(HttpMethod.Get,
"http://msdn.microsoft.com");
var response = await new HttpClient().SendAsync(message,
this.cancellationToken.Token);
string result = await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
this.textBox1.Text = result;
...
(Code Snippet – Async Lab - Ex03 - StartClick with CancellationToken - VB)
Visual Basic
Private Async Sub Start_Click(sender As System.Object, e As
System.Windows.RoutedEventArgs) Handles Start.Click
Me.cancellationToken = New CancellationTokenSource()
Dim uris As New ObservableCollection(Of String)()
Me.Start.IsEnabled = False
Try
Dim message = New HttpRequestMessage(HttpMethod.Get,
"http://msdn.microsoft.com")
Dim response = Await (New HttpClient()).SendAsync(message,
Me.cancellationToken.Token)
Dim result As String = Await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()
Me.textBox1.Text = result
...
7. Likewise in the DownloadItemAsync method, change the HttpClient.GetAsync call with an
HttpClient.SendAsync call to include a cancellation token.
(Code Snippet – Async Lab - Ex03 - DownloadItemAsync with CancellationToken - CS)
C#
public static async Task<LinkInfo> DownloadItemAsync(Uri itemUri,
CancellationToken cancellationToken)
{
string item;
try
{
HttpClient httpClient = new HttpClient();
httpClient.MaxResponseContentBufferSize = 1000000;
var message = new HttpRequestMessage(HttpMethod.Get, itemUri);
var response = await httpClient.SendAsync(message, cancellationToken);
item = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
...
(Code Snippet – Async Lab - Ex03 - DownloadItemAsync with CancellationToken - VB)
Visual Basic
Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri, ByVal
cancellationToken As CancellationToken) As Task(Of LinkInfo)
Dim item As String
Try
Dim httpClient As New HttpClient
httpClient.MaxResponseContentBufferSize = 1000000
Dim message = New HttpRequestMessage(HttpMethod.Get, itemUri)
Dim response = Await httpClient.SendAsync(message, cancellationToken)
item = Await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()
...
8. In the Start_Click method, modify call to the DownloadItemAsync to pass the cancellation
token. Replace the ListBox’s ItemsSource property assignment with the following code:
(Code Snippet – Async Lab - Ex03 - Bind ListBox To Link Info - CS)
C#
this.listBox1.ItemsSource = await Task.WhenAll(from uri in uris select
DownloadItemAsync(new Uri(uri), this.cancellationToken.Token));
(Code Snippet – Async Lab - Ex03 - Bind ListBox To Link Info - VB)
Visual Basic
Me.listBox1.ItemsSource = Await Task.WhenAll( _
From uri In uris _
Select DownloadItemAsync(New
Uri(uri), Me.cancellationToken.Token))
9. In the Start_Click try-catch block, add a new handler to catch the OperationCanceledException
exception. Show a message box to tell the user that the operation was cancelled.
(Code Snippet – Async Lab - Ex03 - OperationCanceledException Handler - CS)
C#
}
catch (OperationCanceledException)
{
MessageBox.Show("Operation Cancelled");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
(Code Snippet – Async Lab - Ex03 - OperationCanceledException Handler - VB)
Visual Basic
Catch exCancel As OperationCanceledException
MessageBox.Show("Operation Cancelled")
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
10. Open MainWindow.xaml Design View. Add a new button and set the name and the content to
Cancel. Modify the margin of the first button to ensure that the buttons do not overlap. Your
XAML in MainWindow.xaml should look something like this after you add the button:
XAML
<Grid>
...
<Button Content="Start" Height="23" HorizontalAlignment="Left"
Margin="272,406,0,0" Name="Start" VerticalAlignment="Top" Width="75"
Click="Start_Click" />
<Button Content="Cancel" Height="23" HorizontalAlignment="Left"
Margin="360,406,0,0" Name="Cancel" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
11. Double-click the Cancel button to add an event handler.
12. In the Cancel_Click event handler, call the cancellation token’s Cancel method.
(Code Snippet – Async Lab - Ex03 - Cancel - CS)
C#
private void Cancel_Click(object sender, RoutedEventArgs e)
{
this.cancellationToken.Cancel();
}
(Code Snippet – Async Lab - Ex03 - Cancel - VB)
Visual Basic
Private Sub Cancel_Click(sender As Object, e As RoutedEventArgs) Handles
Cancel.Click
Me.cancellationToken.Cancel()
End Sub
13. Press F5 to run the application, and then, click the Start button.
14. Press the Cancel button. Notice you can now cancel the operation before it completes. The list
on the right will only show content for the links that it had finished downloading before the
cancellation occurred.
Note: The code that fetches the content for each link does not currently make a distinction
between different kinds of errors—if it started an attempt to download a link’s content, and
that attempt then fails for any reason, the application reports “Not found”. It does this even if
the attempt failed due to cancellation.
So when you click the Cancel button, you will still see plenty of items in the listbox, but it will
show “No content” for each of the download operations that were cancelled.
Task 2 – Polling the Linked Pages to Detect Changes
In this task, you will add code that downloads the content for each of the links repeatedly to detect
changes. It will change the color of the corresponding list entry any time the content referred to by a
link has a different size from the content fetched when you initially click Start.
Note: since this step repeatedly fetches all of the content for everything that the MSDN home
page links too (typically over a hunred pages), this will consume a fair amount of bandwidth.
So if you’re on a connection with a limited download allowance (as is usually the case with
cellular data plans, and also on some broadband packages) you should not leave the program
running for long.
1. Replace all the code of LinkInfo class with the following code. You will implement
INotifyPropertyChanged, which will enable WPF to update bindings automatically each time a
property changes. Additionally, you will be adding a new property named Color.
(Code Snippet – Async Lab - Ex03 - LinkInfo - CS)
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Windows.Media;
namespace AsyncLab
{
public class LinkInfo : INotifyPropertyChanged
{
private string html;
private string title;
private int length;
private Color color;
public event PropertyChangedEventHandler PropertyChanged;
public string Title
{
get
{
return this.title;
}
set
{
this.title = value;
this.NotifyPropertyChanged("Title");
}
}
public string Html
{
get
{
return this.html;
}
set
{
this.html = value;
this.NotifyPropertyChanged("Html");
}
}
public int Length
{
get
{
return this.length;
}
set
{
this.length = value;
this.NotifyPropertyChanged("Length");
}
}
public Color Color
{
get
{
return this.color;
}
set
{
this.color = value;
this.NotifyPropertyChanged("Color");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new
PropertyChangedEventArgs(propertyName));
}
}
}
}
(Code Snippet – Async Lab - Ex03 - LinkInfo - VB)
Visual Basic
Imports System.ComponentModel
Imports System.Windows.Media
Public Class LinkInfo
Implements INotifyPropertyChanged
Private mHtml As String
Private mTitle As String
Private mLength As Integer
Private mColor As Color
Public Event PropertyChanged(sender As Object, e As
PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Public Property Html() As String
Get
Return Me.mHtml
End Get
Set(ByVal value As String)
Me.mHtml = value
Me.NotifyPropertyChanged("Html")
End Set
End Property
Public Property Title() As String
Get
Return Me.mTitle
End Get
Set(ByVal value As String)
Me.mTitle = value
Me.NotifyPropertyChanged("Title")
End Set
End Property
Public Property Length() As Integer
Get
Return Me.mLength
End Get
Set(ByVal value As Integer)
Me.mLength = value
Me.NotifyPropertyChanged("Length")
End Set
End Property
Public Property Color() As Color
Get
Return Me.mColor
End Get
Set(ByVal value As Color)
Me.mColor = value
Me.NotifyPropertyChanged("Color")
End Set
End Property
Private Sub NotifyPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
2. In the MainWindow.xaml file, modify the DataTemplate resource to include set the TextBlock’s
Background property using a SolidColorBrush with its Color attribute to the LinkInfo class’s
Color property.
XAML
<Window.Resources>
<DataTemplate x:Key="DataTemplateItem">
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.Background>
<SolidColorBrush Color="{Binding Color}" />
</TextBlock.Background>
<TextBlock.Text>
3. Open the MainWindow.xaml.cs or MainWindows.xaml.vb file and add an async method called
PollItem. It will receive a Uri, a LinkInfo and a CancellationToken.
(Code Snippet – Async Lab - Ex03 - PollInfo Method - CS)
C#
private static async void PollItem(Uri itemUri, LinkInfo link,
CancellationToken cancellationToken)
{
}
(Code Snippet – Async Lab - Ex03 - PollInfo Method - VB)
Visual Basic
Private Shared Async Sub PollItem(ByVal itemUri As Uri, ByVal link As
LinkInfo, ByVal cancellationToken As CancellationToken)
End Sub
4. Implement the PollItem method to poll the URI every 5 seconds and check for changes in the
content. Each time a change is found, it updates the LinkInfo by changing the color property
with a random color.
(Code Snippet – Async Lab - Ex03 - PollInfo Implementation - CS)
C#
private static async void PollItem(Uri itemUri, LinkInfo link,
CancellationToken cancellationToken)
{
Random r = new Random();
HttpClient httpClient = new HttpClient();
httpClient.MaxResponseContentBufferSize = 1000000;
try
{
while (true)
{
await Task.Delay(5000, cancellationToken);
var requestMessage = new HttpRequestMessage(HttpMethod.Get, itemUri);
var response = await httpClient.SendAsync(requestMessage,
cancellationToken);
string item = await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
if (item.Length != link.Length)
{
link.Title = GetTitle(item);
link.Length = item.Length;
link.Html = item;
link.Color = Color.FromArgb((byte)255, (byte)r.Next(256),
(byte)r.Next(256), (byte)r.Next(256));
}
}
}
catch
{
}
}
(Code Snippet – Async Lab - Ex03 - PollInfo Implementation - VB)
Visual Basic
Private Shared Async Sub PollItem(ByVal itemUri As Uri, ByVal link As
LinkInfo, ByVal cancellationToken As CancellationToken)
Dim r As New Random()
Dim httpClient As New HttpClient
httpClient.MaxResponseContentBufferSize = 1000000
Try
Do
Await Task.Delay(5000, cancellationToken)
Dim requestMessage = New HttpRequestMessage(HttpMethod.Get, itemUri)
Dim response = Await httpClient.SendAsync(requestMessage,
cancellationToken)
Dim item As String = Await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()
If item.Length <> link.Length Then
link.Title = GetTitle(item)
link.Length = item.Length
link.Html = item
link.Color = Color.FromArgb(Convert.ToByte(255),
Convert.ToByte(r.Next(256)), Convert.ToByte(r.Next(256)),
Convert.ToByte(r.Next(256)))
End If
Loop
Catch
End Try
End Sub
Note: Task.Delay is the asynchronous version of Thread.Sleep. It returns a Task object that
completes after the specified amount of time. This enables you to wait for a specified length of
time, but unlike Thread.Sleep, this will not block a thread for the duration.
5. In the DownloadItemAsync function, add a call to PollItem.
(Code Snippet – Async Lab - Ex03 - PollInfo Call - CS)
C#
public static async Task<LinkInfo> DownloadItemAsync(Uri itemUri,
CancellationToken cancellationToken)
{
...
LinkInfo linkInfo = new LinkInfo { Length = item.Length, Title =
GetTitle(item), Html = item };
PollItem(itemUri, linkInfo, cancellationToken);
return linkInfo;
}
(Code Snippet – Async Lab - Ex03 - PollInfo Call - VB)
Visual Basic
Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri, ByVal
cancellationToken As CancellationToken) As Task(Of LinkInfo)
...
Dim linkInfo As LinkInfo = New LinkInfo() With {.Length = item.Length, .Html
= item, .Title = GetTitle(item)}
PollItem(itemUri, linkInfo, cancellationToken)
Return linkInfo
End Function
6. Press F5 to run the application, and then click the Start button. Notice the background color of
the list items change when the application detects an update. Commented [A1]: I am not seeing updates after several minutes. How long should I wait?
Figure 3
Polling pages for content changes
Note: The list may take a while to populate. This is due to the use of Task.Delay in the PollItem
method. Shorten the delay if you think it is taking too long.
Exercise 4: Async Library Refactoring
In this exercise, you will refactor the solution in order to move the asynchronous logic to a class library,
using the proper naming conventions for asynchronous methods that conform to the TAP. Additionally,
you will improve the performance by avoiding switching back to the User Interface (UI) thread when it is
not necessary.
Task 1 – Creating an Asynchronous Class Library
1. Open Visual Studio 2013 and load AsyncLab-Ex4-Begin.sln solution located on your desktop in
the folder TechEd 2014\Asynchronous Programming in the .NET
Framework\Source\[CS|VB]\Ex2-Concurrency\Begin.solution located in the
Source\[CS|VB]\Ex4-Refactoring\Begin folder of this lab. Alternatively, you can continue
working with the solution you created in Exercise 3.
2. Create a new Class Library project in the same solution. Name the new project
AsyncLabLibrary. Then, delete Class1 (which Visual Studio creates automatically when you add
a new class library project).
3. Add a reference to PresentationCore and System.Net.Http.
4. Copy the LinkInfo class from the AsyncLab project into the AsyncLabLibrary library, and then
delete the original LinkInfo file from the AsyncLab project.
5. If you are using C#, rename the namespace in the LinkInfo.cs file from AsyncLab to
AsyncLabLibrary.
6. Add a reference to the AsyncLabLibrary library in the AsyncLab project.
7. In AsyncLabLibrary project, add a new class and name it Downloader.
8. If you are using C#, add a public class access modifier to the Downloader class.
9. Include the following namespace directives:
(Code Snippet – Async Lab - Ex04 - References - CS)
C#
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Media;
(Code Snippet – Async Lab - Ex04 - References - VB)
Visual Basic
Imports System.Net.Http
Imports System.Text.RegularExpressions
Imports System.Threading
Imports System.Windows.Media
Task 2 – Refactoring the Project
1. Open MainWindow.xaml.cs or MainWindow.xaml.vb from the AsyncLab project.
2. Move the DownloadItemAsync, PollItem and GetTitle methods from the MainWindow.xaml.cs
or MainWindow.xaml.vb file to the Downloader class.
3. Rename the DownloadItemAsync method to DownloadItemAsyncInternal. If you are working
in C#, use Visual Studio Refactoring to rename any reference to the method.
4. Rename the PollItem method to PollItemAsync. (This is a public method, and public methods
that conform to the TAP should end with Async.) If you are working in C#, use Visual Studio
Refactoring feature to rename any reference to the method. Otherwise, rename the PollItem
method call to PollItemAsync in the DownloadItemAsync method.
5. Create a DownloadItemAsync public method. The method will receive an URI and a
CancellationToken to support cancellation and call DownloadItemAsyncInternal.
(Code Snippet – Async Lab - Ex04 - DownloadItemAsync - CS)
C#
public static Task<LinkInfo> DownloadItemAsync(Uri itemUri, CancellationToken
cancellationToken)
{
if (itemUri == null)
{
throw new ArgumentNullException("itemUri");
}
return DownloadItemAsyncInternal(itemUri, cancellationToken);
}
(Code Snippet – Async Lab - Ex04 - DownloadItemAsync - VB)
Visual Basic
Public Shared Function DownloadItemAsync(ByVal itemUri As Uri, ByVal
cancellationToken As CancellationToken) As Task(Of LinkInfo)
If itemUri Is Nothing Then
Throw New ArgumentNullException("itemUri")
End If
Return DownloadItemAsyncInternal(itemUri, cancellationToken)
End Function
6. Create an overload method for DownloadItemAsync without the cancellation token parameter.
(Code Snippet – Async Lab - Ex04 - DownloadItemAsync No Cancellation - CS)
C#
public static Task<LinkInfo> DownloadItemAsync(Uri itemUri)
{
return DownloadItemAsync(itemUri, CancellationToken.None);
}
(Code Snippet – Async Lab - Ex04 - DownloadItemAsync No Cancellation - VB)
Visual Basic
Public Shared Function DownloadItemAsync(ByVal itemUri As Uri) As Task(Of
LinkInfo)
Return DownloadItemAsync(itemUri, CancellationToken.None)
End Function
7. Modify the DownloadItemAsyncInternal method to call the ConfigureAwait method at the end
of SendAsync call, passing an argument of false. Add a similar call to the ReadAsStringAsync call
on the next line.
(Code Snippet – Async Lab - Ex04 - DownloadItemAsyncInternal ConfigureAwait- CS)
C#
var message = new HttpRequestMessage(HttpMethod.Get, itemUri);
var response = await httpClient.SendAsync(message,
cancellationToken).ConfigureAwait(false);
item = await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(fals
e);
(Code Snippet – Async Lab - Ex04 - DownloadItemAsyncInternal ConfigureAwait - VB)
Visual Basic
Dim message = New HttpRequestMessage(HttpMethod.Get, itemUri)
Dim response = Await httpClient.SendAsync(message,
cancellationToken).ConfigureAwait(False)
item = Await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(Fals
e)
Note: If you use the await keyword on the UI thread, by default it ensures that when the
awaited operation completes, the remainder of the method runs on the UI thread (even in
situations where the asynchronous work ended up using thread pool threads). If this is not
needed, call ConfigureAwait(false). This can improve performance because it allows work to
continue on some other thread if that would be more efficient. (The default behavior of
completing the method on the UI thread can cause two problems: 1) if the UI thread is already
busy, the work will be delayed, 2) if the rest of the method is slow, running it on the UI thread
will reduce the application’s responsiveness.)
8. Modify the PollItemAsync method to call ConfigureAwait at the end of the SendAsync call,
passing an argument of false. Add a similar call to the ReadAsStringAsync call on the next line.
(Code Snippet – Async Lab - Ex04 - PollItem ConfigureAwait- CS)
C#
private async void PollItemAsync(Uri itemUri, LinkInfo link, CancellationToken
cancellationToken)
{
Random r = new Random();
HttpClient httpClient = new HttpClient();
httpClient.MaxResponseContentBufferSize = 1000000;
try
{
while (true)
{
await Task.Delay(5000, cancellationToken);
var requestMessage = new HttpRequestMessage(HttpMethod.Get, itemUri);
var response = await httpClient.SendAsync(requestMessage,
cancellationToken).ConfigureAwait(false);
string item = await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(
false);
if (item.Length != link.Length)
{
link.Title = GetTitle(item);
link.Length = item.Length;
link.Html = item;
link.Color = Color.FromArgb((byte)255, (byte)r.Next(255),
(byte)r.Next(255), (byte)r.Next(255));
}
}
}
catch
{
}
}
(Code Snippet – Async Lab - Ex04 - PollItem ConfigureAwait - VB)
Visual Basic
Private Async Sub PollItemAsync(ByVal itemUri As Uri, ByVal link As LinkInfo,
ByVal cancellationToken As CancellationToken)
Dim r As New Random()
Dim httpClient As New HttpClient
httpClient.MaxResponseContentBufferSize = 1000000
Try
Do
Await Task.Delay(5000, cancellationToken)
Dim requestMessage = New HttpRequestMessage(HttpMethod.Get, itemUri)
Dim response = Await httpClient.SendAsync(requestMessage,
cancellationToken).ConfigureAwait(False)
Dim item As String = Await
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(
False)
If item.Length <> link.Length Then
link.Title = GetTitle(item)
link.Length = item.Length
link.Html = item
link.Color = Color.FromArgb(Convert.ToByte(255),
Convert.ToByte(r.Next(255)), Convert.ToByte(r.Next(255)),
Convert.ToByte(r.Next(255)))
End If
Loop
Catch
End Try
End Sub
9. Open MainWindow.xaml.cs or MainWindow.xaml.vb code-behind file and import the
AsyncLabLibrary namespace.
(Code Snippet – Async Lab - Ex04 - References dll - CS)
C#
using AsyncLabLibrary;
(Code Snippet – Async Lab - Ex04 - References dll - VB)
Visual Basic
Imports AsyncLabLibrary
10. In the Start_Click method, replace the ListBox polling code to call the new DownloadItemAsync
method from the downloader library.
(Code Snippet – Async Lab - Ex04 - DownloadItemAsync Call - CS)
C#
this.listBox1.ItemsSource = await
Task.WhenAll(from uri in uris select Downloader.DownloadItemAsync(new
Uri(uri), this.cancellationToken.Token));
(Code Snippet – Async Lab - Ex04 - DownloadItemAsync Call - VB)
Visual Basic
Me.listBox1.ItemsSource = Await Task.WhenAll( _
From uri In uris _
Select
Downloader.DownloadItemAsync(New Uri(uri), Me.cancellationToken.Token))
11. Press F5 to run the application. You will notice no difference in the visible behavior. The UI is
still responsive while the application downloads content for links. ConfigureAsync provides a
performance improvement, as it avoids marshaling control back to the UI thread once
asynchronous operations complete (although in practice, you are unlikely to be able to notice
any difference in the visible performance of this application).
Appendix: Using Code Snippets
With code snippets, you have all the code you need at your fingertips. The lab document will tell you
exactly when you can use them, as shown in the following figure.
Figure 1
Using Visual Studio code snippets to insert code into your project
1. Right-click where you want to insert the code snippet.
2. Select Insert Snippet followed by My Code Snippets.
3. Pick the relevant snippet from the list, by clicking on it.
Figure 2
Right-click where you want to insert the code snippet and select Insert Snippet
Figure 3
Pick the relevant snippet from the list, by clicking on it