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 processing in the background. Then, you will add the ability to poll the content of each of the links retrieved and check for modifications. In that case, 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 in the application in order to terminate ongoing tasks.
- Open Visual Studio 11 and load the AsyncLab-Ex3-Begin.sln solution located in the Source\[CS|VB]\Ex3-PollingAndCancellation\Begin folder of this lab. You can also continue working with the solution you’ve obtained after completing Exercise 2.
Open MainWindow.xaml.cs or MainWindow.xaml.vb and import the System.Threading namespace.
using System.Threading;
Imports System.Threading
Define a class variable to create the cancellation token.
(Code Snippet – Async Lab - Ex03 - Cancellation Token - CS)
public partial class MainWindow : Window
FakePre-d1aa6c30e95b4647829a35e77a470b6e-b418c9480abe4e29b03f87aaedb4ebc7 private CancellationTokenSource cancellationToken;
(Code Snippet – Async Lab - Ex03 - Cancellation Token - VB)
Class MainWindow
Private cancellationToken As CancellationTokenSource
Modify the DownloadItemAsync method to receive a CancellationToken parameter.
private static async Task<LinkInfo> DownloadItemAsync( Uri itemUri, CancellationToken cancellationToken) {
Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri, ByVal cancellationToken As CancellationToken) As Task(Of LinkInfo)
Create a new instance of the cancellation token inside the startButton_Click event handler.
(Code Snippet – Async Lab - Ex03 - Cancellation Token Instance - CS)
private async void startButton_Click(object sender, RoutedEventArgs e)
FakePre-4a10452bb2fa431a827729d33a2aaf59-ea4da44e722249e6bcff05dd97e59cc3 cancellationToken = new CancellationTokenSource();FakePre-baca74d635e14b6887be3b535b943202-ee9078fea7d0423482f78ce2feddbb0a
(Code Snippet – Async Lab - Ex03 - Cancellation Token Instance - VB)
Private Async Sub startButton_Click(sender As System.Object,
FakePre-d4af7471bcf94e28b2f3e4c3572cc30e-b1006ab7cb604dadbc270397332c3450FakePre-02f61911905146299c1d5fdf3aee74fe-beb9f8eafeab4c919a34a9c5ba63b08f cancellationToken = New CancellationTokenSource()FakePre-4a2ebccd1a4e4d29a8bdb6e7a0914fe5-d880dc5abed84de8aff1b8e727346da7
In the startButton_Click method, modify the HttpClient call to include a cancellation token. Then, replace the GetAsync call with a SendAsync call, which supports cancellation tokens.
(Code Snippet – Async Lab - Ex03 - StartClick with CancellationToken - CS)
try
FakePre-329be8ad65604494a79221d03e760dd9-4ec92160bad04b3abc0976f5697a764e HttpRequestMessage message = new HttpRequestMessage( HttpMethod.Get, "https://msdn.microsoft.com"); var response = await new HttpClient().SendAsync(message, cancellationToken.Token);FakePre-fb146b823bbf4454973abde2ec175367-98350d4ef2bb4728b8f5634f564f3b9dFakePre-9943207bc9bd4bf4b4c36cd55ae7f28b-9efb011a7ff44461aa6cc99018ee3807FakePre-31546d3109184555bf525e155c9a13aa-a32ac25494fb486aa0d9eee7045bc637
(Code Snippet – Async Lab - Ex03 - StartClick with CancellationToken - VB)
Try
Dim message = New HttpRequestMessage(HttpMethod.Get, "https://msdn.microsoft.com") Dim response = Await (New HttpClient()).SendAsync(message, cancellationToken.Token)FakePre-bdbdc6d239dd48c6ad0efc0fd9ecfc0e-d8b8993a00374129a16de7103596451bFakePre-17cc9753dadb4319a27452b4f9f43396-1ce32484781047bda678be2a15f4976dFakePre-de524866afa04c9e88559639b523d9bb-403dd43cfc6948be95be52558ae6bf2a
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)
try
FakePre-d05abb2022a1486ca4c253e2c1ec648a-728e5b7324a9405a867ac4c518e4134dFakePre-6a181d7efe5d442c8a0ba278dfd59dc7-492af626080d4c0da4656365e8acfc01FakePre-7d2a8e78405146f69389a8feb60d9cd9-93087c985ca94ccc87cc7c6c831e24baFakePre-1ca1783e657e4aefa049637bfd647b1b-9adc129fc70b4b6d96c3685d61388db7 var message = new HttpRequestMessage(HttpMethod.Get, itemUri); var response = await httpClient.SendAsync(message, cancellationToken);FakePre-f9a0b949cc3d435ea9c63098ce72fcfa-9c9e9155919c43d899b7bb2b3d60aa59FakePre-45bfd77c6e3c443b9ff9ceb357000f91-cf3d3be8eb5b4cd1b139a87cab9bcc16
(Code Snippet – Async Lab - Ex03 - DownloadItemAsync with CancellationToken - VB)
Try
FakePre-f417090f4d80420dbec93603746efb50-651d7f1d5c4f459abf816970dea359ddFakePre-b509eeabda3247c4ba7a93f182918b9f-871ecd4428c34c9fba1d5a9c2dba7f69FakePre-a3ced528db764f0488faf99eb4533301-abd0b858b0304425bee5acb046142cd1 Dim message = New HttpRequestMessage(HttpMethod.Get, itemUri) Dim response = Await httpClient.SendAsync(message, cancellationToken)FakePre-18271bfed0884bb4a69ab4ff24b635f9-856e8cda00254c02a0ef6091cedac071FakePre-6c40003487904aeb941cbac8a81906d1-47312532adc24f43ae6ee9fb60792109
In the startButton_Click method, modify the DownloadItemAsync call in order to send 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)
listBox1.ItemsSource = await Task.Run(() => { return Task.WhenAll(from uri in uris select DownloadItemAsync( new Uri(uri), cancellationToken.Token)); });
(Code Snippet – Async Lab - Ex03 - Bind ListBox To Link Info - VB)
listBox1.ItemsSource = Await Task.Run(Async Function() Return Await Task.WhenAll( From uri In uris Select DownloadItemAsync(New Uri(uri), cancellationToken.Token)) End Function)
In the startButton_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)
}
catch (OperationCanceledException) { MessageBox.Show("Operation Cancelled"); }FakePre-da406334b90847c48f7ba0ced59596b3-a46f1b5f72c34cd4ad62466c56744499FakePre-6179ed0ce692493b8ba54ce8440d8a53-acdec413b4064394a2ee4ff7218d5014FakePre-92020d3ddbc3481d9666a8dd894758f8-61d6127ac68a49b494f03e1c792f9a83FakePre-00b2284292ee407aabc93e7169c65cec-cafd8b2447b245edbb6dc3bed0476e31
(Code Snippet – Async Lab - Ex03 - OperationCanceledException Handler - VB)
Catch exCancel As OperationCanceledException MessageBox.Show("Operation Cancelled") Catch ex As Exception
FakePre-b7f743fe71c9415e986e62fbdd14328d-58a0cdb3965e4318af2bbb9b94131e53FakePre-bcbf09d3dd7144eb8091ce9da11bbdee-23dabefb5e8447d28a8defd432228213
Open MainWindow.xaml Design View. Add a new button and set the name and the content to Cancel. If the buttons are not aligned, use the design view to place the buttons correctly in the grid. This is the XAML code of MainWindow.xaml you should obtain after the button is inserted:
<Grid>
FakePre-bb074ca5b7714b8e8f9b5e5f12d27253-a0657b2b14114d5c9c07e26a0dcfca91FakePre-33c4fa694dff4edf9584f2a17ca3fcfd-838afe2b6697458bb6ed7e71123b7dc2FakePre-93f68f9ce6e24b7e8841f5ec32ac4baf-8ed26e4b23d74074a840796d953508c0FakePre-c3aba4f214fb4dc4b11373b0eaae1bd9-48d9728d685245c388480b05bc24bcaeFakePre-bd6e981cab29413584d3e27aee40e56e-af5d910173bf42608915e0b8c3b363b0 <Button Content="Cancel" Height="23" HorizontalAlignment="Left" Margin="318,406,0,0" Name="cancelButton" VerticalAlignment="Top" Width="75" />FakePre-bbd13b006b95451997918e2a46d8052a-e4222598d92e4b9887e33d8ee0f259f4FakePre-f81fd216b4664c83934632f5eecf89fb-737cee2a58064d87a9c3883102a4cca4
- Double-click the Cancel button to add an event handler.
In the Cancel_Click event handler, call the Cancel method from the cancellation token.
(Code Snippet – Async Lab - Ex03 - Cancel - CS)
private void Cancel_Click(object sender, RoutedEventArgs e)
FakePre-4651ce00c9d14ef8902e267d7ebd35c5-152f0a7454d645fa9e72e0643a2abbd9 cancellationToken.Cancel();FakePre-a32d4cbe27184391b2553bc8f0197893-6bbafb1fa8ad43f5b16a05e1f26edb1f
(Code Snippet – Async Lab - Ex03 - Cancel - VB)
Private Sub Cancel_Click(sender As Object,
FakePre-f4593990cf7d43b4bfc7b030543ab5ec-d3e5273822274047837ffdc1d6dbf6ffFakePre-fce2423dfd42440ab901089f3856b863-fc1f83d6b19b449a926d2bc71a832296 cancellationToken.Cancel()FakePre-8d85cc6ac21a4797bcd31df33e6aacce-2192f926230b473783f075e0838e39b5
- Press F5 to run the application, and then, click the Start button.
Press the Cancel button. Notice you can now cancel the operation before it completes. The list on the right will only show the links that were requested before the cancellation occurred.
Figure 3
Application updated with Cancel button
Task 2 – Polling the Links Status
In this task, you will add the ability to poll the links status and use different colors when their content has changed.
Replace all the code of LinkInfo class with the following code. You will implement INotifyPropertyChanged, taking advantage of the real time binding. Additionally, you will be adding a new property named Color. Notice that the set was implemented in all the properties to notify the changes.
(Code Snippet – Async Lab - Ex03 - LinkInfo - CS)
namespace AsyncLab { using System.ComponentModel; using System.Windows.Media; 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 title; } set { title = value; NotifyPropertyChanged("Title"); } } public string Html { get { return html; } set { html = value; NotifyPropertyChanged("Html"); } } public int Length { get { return length; } set { length = value; NotifyPropertyChanged("Length"); } } public Color Color { get { return color; } set { color = value; NotifyPropertyChanged("Color"); } } private void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } } FakePre-a29870789639478cbb371658c19e4bbe-2ff5cd71eb604c06a7c1c6037fbd3613FakePre-dffe8d47339d4e5e8b4caeea9b18eea2-9222fb3e09c34525bf48133e9a5a6f35FakePre-e50e20af859243e880fc056373498b22-17d432a9593c4977b00208bbec05bb77FakePre-3dbb95cf322c4e4fbc1cb5141f87696f-3a39571c2356485d8a1af05dc5d6d523FakePre-1940659354ea45c38a72ca6a89b017ae-670ded56bf54462ab0bd4e58c751f01aFakePre-fa42c771cbbb451a98a56d017af63a21-4c91e01385d94bc6b52482bdeec0e647FakePre-03dbb52b860f46c08e0f855f7597cd5f-175d0c7c297a47c198986fb59267ae49FakePre-ef8f90289aa4419caac3f286846fd2c0-363efc7e8cb24c1e905919e17f6a706d
(Code Snippet – Async Lab - Ex03 - LinkInfo - VB)
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 mHtml End Get Set(ByVal value As String) mHtml = value NotifyPropertyChanged("Html") End Set End Property Public Property Title() As String Get Return mTitle End Get Set(ByVal value As String) mTitle = value NotifyPropertyChanged("Title") End Set End Property Public Property Length() As Integer Get Return mLength End Get Set(ByVal value As Integer) mLength = value NotifyPropertyChanged("Length") End Set End Property Public Property Color() As Color Get Return mColor End Get Set(ByVal value As Color) mColor = value NotifyPropertyChanged("Color") End Set End Property Private Sub NotifyPropertyChanged(propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End Class FakePre-7d06394a6eb341f2bbbe5f110b4ffe5a-6d73a12a179d4fc4ae3701c5053b3e12FakePre-515f66a64a6549249c07c44095824361-3e2a934b0f8c4665b8ad6b2a044c253aFakePre-7afa6ff95dbc4764813b68e6619b572f-b5571a72f934468dbb3db450922bba0bFakePre-ac122ee584bc45ee82aed4b7be608c6b-abdc3b4c2d8745d99f941232627cd18cFakePre-5b707da841e64175aa9ce00d2f8ebba9-3c3637376aa0490d8b3e9fbb01a73909FakePre-d6a652468d724498a8495afdbecf52a6-d89241ffd5ef41bda1d0397bfb8f0e95FakePre-4bfda61e86dd4e5b94a09cb4ff7271f0-82833d52ccad424dbf6d74cc1089eabe
In the MainWindow.xaml file, modify the DataTemplate resource to include a SolidColorBrush in the TextBlock.Background and bound the Color attribute to the Color property.
<Window.Resources>
FakePre-8b4049864c424442959cfd31ab47a25e-61edbcc18eec4d749c0268a7a28f682cFakePre-126c3ba089714b9a8dc46fe05c5d5a46-a860a55bb15343a2a2926e95ed336e45FakePre-6361877fd0204c3a9d06d14d5379f020-29ad4120fa684e79843d71f223778c80 <TextBlock.Background> <SolidColorBrush Color="{Binding Color}" /> </TextBlock.Background>FakePre-b8eb7e5cb12f4fb5aee1bc02aef1bb32-58d736cb8d77449ca6ca96fd72e012b6
Open the MainWindow.xaml.cs or MainWindows.xaml.vb file and add the PollItem async method. It will receive an URI, a LinkInfo and a CancellationToken.
(Code Snippet – Async Lab - Ex03 - PollInfo Method - CS)
private static async void PollItem( Uri itemUri, LinkInfo link, CancellationToken cancellationToken) { }
(Code Snippet – Async Lab - Ex03 - PollInfo Method - VB)
Private Shared Async Sub PollItem(ByVal itemUri As Uri, ByVal link As LinkInfo, ByVal cancellationToken As CancellationToken) End Sub
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)
private static async void PollItem(Uri itemUri, LinkInfo link,
FakePre-a5fe77d13d824e398cee0b8a7921d796-63fc669494004027b480c71a59b57d39FakePre-68663456677a46c2a7514f295c9215d1-97af77ccc7104c239580a8fcbeb066ad 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.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 { }FakePre-2cf3570b8ef847d1b619f0c4683e7cb9-00b81635f0664242a553db3cfc28b510FakePre-aa6a8b86a9d444ce9b62f538ee659e96-2ed4b06eb99d4b40ad498a171579ce20FakePre-d0ee8847850540d5b1d13c5cdcd3a0c4-4da0bee94d864e2ca9b19ef22e367922
(Code Snippet – Async Lab - Ex03 - PollInfo Implementation - VB)
Private Shared Async Sub PollItem(ByVal itemUri As Uri,
FakePre-1603026e8ded4a7084585bc5a52faee7-160252a01ab84cc19439f0a4b5e89ec5FakePre-2342cf2c95bc493c8c4890765a8b5261-59f91ad848704c68b889191504af3cad 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.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 TryFakePre-7a3896728b114aa6b04add27ef2efdf7-6a0ca6bb512647fabfccfe77d527ca21FakePre-3eaa709d8ec24eb5811eddad6326da09-1e895524402d4e8d8f14413036228e08
Task.Delay is the asynchronous version of Thread.Sleep. It returns a Task object, which completes after the specified amount of time.
In the DownloadItemAsync function, add a call to PollItem.
(Code Snippet – Async Lab - Ex03 - PollInfo Call - CS)
public static async Task<LinkInfo> DownloadItemAsync(Uri itemUri,
FakePre-a20983ee797f4240890729ba3e28184b-62f15350b4f846e9a291cd2a36e44c1aFakePre-335766c5c7594579b6c7cc020a11063d-8b4245689a8a4b42a6c59c4f1faa76f8FakePre-e41a5d74093449b4a0f7b794b31cb45b-1347c9ff9c5f432b86b738ffdb6e5ac4FakePre-d6781c6406614436a2610416e225a074-681b666b1b3240759dd5c871ecd19526FakePre-3104a610c41a4f9aa8b204df7345f01f-30792bf801a942a9a64c1e6b1b4c6f89FakePre-d7830a7d982f486385cad440ab6f7bdd-89863ed86df643bea830fe457aab9776FakePre-0b3b8d1741134dc3b03843b0c315cdf2-0b50ba1ecec14116952859d0e701d7a7 PollItem(itemUri, linkInfo, cancellationToken);FakePre-1e35ca3afe354782b1b10c3f2ea2cc77-c14a2005f9604a70b37783fc68abb126FakePre-a6377c4fc27d494592fc746a77d1e1b8-e264376002ef4273a70bd4c83f3c807eFakePre-4c5333a476ae4b18b4988b74663b2977-aa46f856c93b440b83ad1902d3dfdf79
(Code Snippet – Async Lab - Ex03 - PollInfo Call - VB)
Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri,
FakePre-af6fb6c0660e47f8943b06a431bd9cad-b96b9d1308bb430fbbf36647772c3c2eFakePre-8abd1ab7f1324cdeb749c02abac0479c-69642b2f444048f4abf0bf9d909ed73aFakePre-728ddc7b79d24595a4cd746e7c2ebfee-9e2b5093f953401bb936fe0d65c80ae9FakePre-a7d6e85cc9894157a4d3e268f38cdf82-a1772c5c718a4237b5df79e41811119eFakePre-f77366b8afae4f27a63af03278c0c078-2831ecaa97e7496aa4e1b743bb5baefbFakePre-a3686891e9044110b082b980fa749427-9ebec57169b9481181811b30a1a14970 PollItem(itemUri, linkInfo, cancellationToken)FakePre-ed4e4f935d124b4dbed3b5450a9bb63a-a998bf9805a24d5fafdf054682fbccd8FakePre-7d780a546c9943b890eafac26e533625-fc03ff7b963c4ff3bae0bb020b30a1beFakePre-0ae6d27f08624b8fab07ee27cb6d57fc-27def95ddfac4049a4c0bb7c64494dac
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.
Figure 4
Polling links status
|
|