Exercise 1: The Async Method

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. You will build this application in two ways. First, you will build the application using synchronous code and examine the issues with this implementation. Second, you will modify that application to use the asynchronous code so you can see how this implementation addresses 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 link 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 11 and create a new WPF Application project with the name AsyncLab. Choose the language of your preference: C# or Visual Basic.
  2. In the MainWindow.xaml designer, resize the window to 640x480.
  3. Drop a TextBox on the left to display the HTML and a ListBox on the right to display the found links.
  4. Drop a button at the bottom and set its content to “Start”. You will get the following XAML code:

    XAML

    <Window x:Class="MainWindow"
    FakePre-a0297811d7b04c0da622b08ca1cc7281-a3669799cd7f486daaabca90951cd3e1FakePre-3369da8241434470a270f33c8f6cae77-63eaf36c47094a06a72cbf47d1535141FakePre-4fa7cb16e7754b318cb7648289e9a006-0050f50ab76e4b54896c8fb2fea1d73c Width="640" Height="480"> <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=“startButton” VerticalAlignment="Top" Width="75" />FakePre-000d2ba8387d45a98a7e67877859f5d1-74fb9f89a62d4abfad46f6dba2cadfcd Width="640" Height="480"> <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=“startButton” VerticalAlignment="Top" Width="75" />FakePre-c90f3f5763994690ae5d9a04159d75db-71489cfd083a42a9997ece199b351d4aFakePre-ae1507e0f41d40699054e372ce6718e4-d9c45524ff22444092377f2e7a44c2fd
    Note:
    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 startButton_Click(object sender, RoutedEventArgs e)
    FakePre-5ce38126a554471c8c8d6f67b94b148a-b5dcaf38ddb0468bb476b81e90a723d8 startButton.IsEnabled = false; startButton.IsEnabled = true;FakePre-fcf10b29fefc48238577b83e801e0370-d37cdbf6121d46d4a96eaf9be1e63c59

    (Code Snippet – Async Lab - Ex01 - Disable Enable Start - VB)

    Visual Basic

    Private Sub startButton_Click(sender As System.Object, 
    FakePre-9f7a16dc994d4e69914a0d49718cd244-562be642e3114da79baa6101d702269fFakePre-c0ab9957ac26481c80d464776901ae4f-c67a5af37d3f4a30a709b1baff2bd9f7 startButton.IsEnabled = False startButton.IsEnabled = TrueFakePre-02622401af534c598b616c3b8857adfe-2b26c836262049019488bccdf4218e9cFakePre-67ce2f1bd1e84ff8bf3ae5f31a1e0091-15c469904bda49feb4d278e0a448d9c8

  7. Define a list of strings to store the URIs at the beginning of the Start event.

    (Code Snippet – Async Lab - Ex01 - URIs List - CS)

    C#

    private void startButton_Click(object sender, RoutedEventArgs e)
    FakePre-bb4ec430003b4bde952ccbbe8c6210be-d490c8f50eb042c09fcf62b1024bf77d List<string> uris = new List<string>();FakePre-e77038ddf0e44adba070285cd12486c7-32b2a0fe03164a9ebe3ad7f5347d2ffa

    (Code Snippet – Async Lab - Ex01 - URIs List - VB)

    Visual Basic

    Private Sub startButton_Click(sender As System.Object, 
    FakePre-d03a3b0d04d94f46b100e33ba2d6eac6-24f2e30f1e8b4305baac4f641bfac579FakePre-7621396ffa864091ae73107753353025-d37154f3703c4653bd8af1ae46bafcdd Dim uris As New List(Of String)()FakePre-ada0b3600913497083f30746621c9770-2707645574aa4c68b75eafec512c6fa1

  8. In between the code that disables and enables the Start button, insert a try-catch block for the main functionality of the application. Then, implement an exception handler to display a message box with the description of the error.

    (Code Snippet – Async Lab - Ex01 - TryCatch - CS)

    C#

    List<string> uris = new List<string>();
    FakePre-59e13169177a4f9cbbc1d5582e9b7494-225b592ea8a64b47b145ca964467c42bFakePre-e765a1a0092d46e98d14975d7960dd59-95353932af0f4e3391431ee64edd8ad9try { } catch (Exception ex) { MessageBox.Show(ex.ToString()); }FakePre-e1ec5ea22e7a4f8a862c08c86b3280cb-1caa4b3b5232431dbcc12c3e680e8ec3FakePre-05d630696742425887036d6de6c011c5-8a3e73613a084385bac97a583fefe333FakePre-a841b02530d54de28ba7a14de15468c5-e672dc670fd44f2794e53ab380c5cc79

    (Code Snippet – Async Lab - Ex01 - TryCatch - VB)

    Visual Basic

    Dim uris As New List(Of String)()
    FakePre-64e905dba8e24f0ca35bc31c75b8bdb3-a8fbc9c6cf994b55a45d63820172d0e9FakePre-0da1c7cf593a4e63b56a15881b1ceb9f-6a8362353b184cdabfa77ccc743a6cc2Try Catch ex As Exception MessageBox.Show(ex.ToString()) End TryFakePre-848fb7149b0547d2beb9b93275f3d736-84f6133950594696bd04e9d83f07b696FakePre-d65457f5f96047b2b5afae8fbcd209ff-2c8075303e9e4fe48750ac57c5c4d6b6FakePre-3fc5483a28a84cb6b92f6379fc926a98-d1021bf4218b40b984052441300ae7ef

  9. Add a project reference to System.Net.
  10. Import the following namespaces:

    (Code Snippet – Async Lab - Ex01 - Namespaces - CS)

    C#

    using System.Net; using System.Text.RegularExpressions;

    (Code Snippet – Async Lab - Ex01 - Namespaces - VB)

    Visual Basic

    Imports System.Net Imports System.Text.RegularExpressions

  11. Insert the following code to download the HTML content from a specific URL. Then, once it is downloaded, the result is assigned to the textbox value.

    (Code Snippet – Async Lab - Ex01 - Download Page - CS)

    C#

    try
    FakePre-cf0350279926485da182c889987e9ba7-9bea3a0188854499814bb150fbbf8305 WebClient client = new WebClient(); string result = client.DownloadString("https://msdn.microsoft.com"); textBox1.Text = result;FakePre-015bddce1ed6416db2090087e6d73650-75e5f4120208461a931543390a4a4372FakePre-55b7337ca56541c0a26a733cb54194ca-112082fbafcd456c9d722c354ccd99bbFakePre-c4645d0375b9447b868fe409160c2cd7-1ca600700c2843ce85d01375a83d6081FakePre-e68deb4800714547977c56e4d23b2b53-bb57686a5c2b441d9a5f9d5ef06bd1f8FakePre-0648dc03d7c5439a8c5398c671bb50c3-96ef0b5dfe3a4b569129198f31d52b35FakePre-0b7e2175d115484e8f5b324582b448be-deb7d1000bd145619c8bbf6a1cd4ed30FakePre-d87eb877d413443b996ff1dfd116c22d-39da31079b224ee0aa72e616c9c643c1

    (Code Snippet – Async Lab - Ex01 - Download Page - VB)

    Visual Basic

    Try
    Dim client As New WebClient() Dim result As String = client.DownloadString("https://msdn.microsoft.com") textBox1.Text = resultFakePre-7cb90f9fbc3149f3a7eaa6aef73eea85-9f4ec8b394b84113bdb5ed58a84dbfaeFakePre-95aff187d2124b1892bd01821e13707e-1434d1ea5a2b419ea8fbffcaabe53306FakePre-1e11e6adfaa0405c8a9114ca18cd5964-8bf2452901904a27ac4f0c783905f6f9FakePre-31c5974dbe724bed8eafc7d611da395d-e4565aea0c1c4e238451e63158aec50f

  12. Use a regular expression to search for links, store them in a URIs list, and then, bind the URIs list to the ListBox.

    (Code Snippet – Async Lab - Ex01 - Search Links - CS)

    C#

    textBox1.Text = result;
    FakePre-48cb475f157044c295a9911c5ead3866-85d89828cc2c42bdb6fe99ecfda1e9b8MatchCollection mc = Regex.Matches(result, "href\\s*=\\s*(?:\"(?<1>https://[^\"]*)\")", RegexOptions.IgnoreCase); foreach (Match m in mc) { uris.Add(m.Groups[1].Value); } listBox1.ItemsSource = uris;

    (Code Snippet – Async Lab - Ex01 - Search Links - VB)

    Visual Basic

    textBox1.Text = result
    FakePre-6b6d63feb27f4386a8ee0045c99b9de1-eb2304d24b8048c69be5047e2bc13a10Dim mc As MatchCollection = Regex.Matches(result, "href\s*=\s*(?:\""(?<1>https://[^""]*)\"")", RegexOptions.IgnoreCase) For Each m As Match In mc uris.Add(m.Groups(1).Value) Next listBox1.ItemsSource = uris

  13. Press F5 to start debugging. When the application starts, click the Start button.

    Figure 1

    Running the application

    Note:
    Notice the window is unresponsive and the button is not being disabled because the UI is blocked while the application is downloading the web page.

Task 2 – Using Asynchronous Code

In this task, you will enhance the application you have started in the previous task to make it asynchronous. To do this, you will implement an asynchronous method to download the page in the background.

  1. Add a project reference to System.Net.Http.
  2. Make the following change at the top of the code file:

    (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

  3. Add the async keyword in the startButton_Click function.

    C#

    private async void startButton_Click(object sender, RoutedEventArgs e)
    FakePre-04c2e2b80837490285207d7fe8ec7e41-1ce74c3a845d4b7db9adb40ed58313ac

    Visual Basic

    Private Async Sub startButton_Click(sender As System.Object, 
    FakePre-6147d1c3908b44418109fdf89cf51953-15d4e5c0cb77471ab8932da3c4ef40c1

    Note:
    The async keyword is one of the new keywords that .NET Framework 4.5 provides. The Async keyword communicates to the compiler that the current method will contain asynchronous code.

  4. Replace the code that uses the WebClient class with a call to the GetAsync method of the HttpClient class.

    C#

    try
    FakePre-02a68c13f7ee42b9910c8cc7a31dcc95-8c396f6bcb7749dba9038e6e3a7a1988 var response = new HttpClient().GetAsync("https://msdn.microsoft.com"); string result = await response.Content.ReadAsStringAsync();FakePre-6a313059290c468bb2b778dd6b7c9d76-99a9fef93e5a4d3caab227ad6c7b569cFakePre-855c739da8c444c79dba7d7ed50d6315-a91bd58727084f6da31a63c1dbffc5e7FakePre-4ab510a9e1624bcabd819a49f5b8d659-bc0cfd58902b43bb868d116743e416ec

    Visual Basic

    Try
    Dim response = New HttpClient().GetAsync("https://msdn.microsoft.com") Dim result As String = response.Content.ReadAsStringAsync()FakePre-bcf5dd9a49ca4d1dbc1b9fecfc8486b7-5526b9c7ee9b4f269d0ffb31d4535177FakePre-75125e46d8be4c909692d3a0a52c3596-92a0923f28da4a6baed6da19b00e38efFakePre-ba19dce8acb540928556afd2c0ad8e0b-446864dbed7c4531ad289772d527b5ff

    Note:
    The GetAsync method returns a Task object. A Task object represents an asynchronous operation that may complete at some point in the future.

  5. Add the await keyword on the call to GetAsync().

    (Code Snippet – Async Lab - Ex01 - Download Page Async - CS)

    C#

    try
    FakePre-2f7a24b4e7fc4bee83b6876b8fca6ce2-87a2a52ce2ae4b22b0b59a68ed7958f5 var response = await new HttpClient().GetAsync("https://msdn.microsoft.com"); FakePre-0803ffb9265c429cb397bc6f4b6e8795-4025e6a1c1764c6bbded2445377636ecFakePre-d2db2762226b428cab3d1979faeb2e78-e1db192307964750ab30b8eaea998bcdFakePre-ffefa7884a414b32bb4fc8bc12f2fe27-586fc2361a1e4a868b68542673d0c612FakePre-134e46b040eb401e9d6d109ed0a306b2-76bab35ab3094df0bfc79cac5b8c0bcd

    (Code Snippet – Async Lab - Ex01 - Download Page Async - VB)

    Visual Basic

    Try
    Dim response = Await New HttpClient().GetAsync("https://msdn.microsoft.com")FakePre-b5d232c6a28a4f53a89e774b92a90e58-82663bc78fb341379c8c5551ae1d1fc3FakePre-8a7b26bad0f54775a13d16c6a38335f9-dc28e2a9a7364aef83d365505b6f9a98FakePre-6f9d7f6a7ed44c139e54da4e6a280347-1ff67a71cb284f1bbe679cb1d636a369FakePre-9215f287b61f49b990655e07d29d9578-c2e0a637a0c440d18aa72168de471a55

    Note:
    When you add the await keyword, you are telling the compiler to asynchronously wait for the task returned by the method. This means that the rest of the code will be executed as a callback after the awaited method completes. You do not need to change your try-catch block in order to make this work. The exceptions that happen in the background or in the foreground will still be caught.

  6. Press F5 to run the application and click the Start button. Notice that the application is responsive while the Start button is disabled. UI thread is never locked because the work is processed in the background.