Export (0) Print
Expand All
This topic has not yet been rated - Rate this topic

WhenAny: Bridging between the .NET Framework and the Windows Runtime (C# and Visual Basic)

The example in this topic combines a Windows Runtime type that downloads blog feeds asynchronously with a .NET Framework method that processes asynchronous tasks in the order in which they complete. For more information about the type, see SyndicationClient. For more information about the method, see Task.WhenAny.

By combining these features, you can start to download multiple blog feeds simultaneously and process the results as they finish. If one feed downloads more quickly than the others, its results appear first. By using a SyndicationClient method, you can download the feeds more easily; by using the Task.WhenAny method, you can more easily identify the next feed that's finished downloading.

Note Note

To run the example, you must have Windows 8 installed on your computer. In addition, if you want to run the example from Visual Studio, you must also have Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows 8, or Visual Studio Express 2013 for Windows installed.

The following code combines these features from the Windows Runtime and the .NET Framework:

try
{
    IEnumerable<Task<SyndicationFeed>> feedsQuery =
            from uri in uriList
            // AsTask changes the returns from RetrieveFeedAsync into tasks. 
            select client.RetrieveFeedAsync(uri).AsTask();

    // Run the query to start all the asynchronous processes.
    List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();

    SyndicationFeed feed;

    // Repeat the following until no tasks remain: 
    //    - Grab the first one that finishes. 
    //    - Retrieve the results from the task (what the return statement  
    //      in RetrieveFeedAsync returns). 
    //    - Remove the task from the list. 
    //    - Display the results. 
    while (blogFeedTasksList.Count > 0)
    {
        Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
        feed = await nextTask;                    
        blogFeedTasksList.Remove(nextTask);
        DisplayResults(feed);
    }
}
catch (Exception ex)
{
    ResultsTextBox.Text =
        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
}

The example produces output that resembles the following lines. For each blog, the display shows the title of the blog followed by the titles and dates for blog posts.

Developing for Windows
     New blog for Windows 8 app developers, 5/1/2012 2:33:02 PM -07:00
     Trigger-Start Services Recipe, 3/24/2011 2:23:01 PM -07:00
     . . .
     Countdown to PDC10, 10/26/2010 4:11:28 PM -07:00

Extreme Windows Blog
     PDXLAN 20: “Epidemic” Custom PC by Jon Hansz, 7/30/2012 2:31:35 PM -07:00
     Samsung Notebook Series 9: Taking Thin and Light to the Extreme, 7/23/2012 12:06:03 PM -07:00
     . . .
     AMD Unveils A-Series APUs, 6/13/2011 9:34:01 PM -07:00

Blogging Windows
     Windows 8 has reached the RTM milestone, 8/1/2012 9:00:00 AM -07:00
     Windows 8 will be available on…, 7/18/2012 1:09:00 PM -07:00
     . . .
     More buzz from BUILD – Developers get their devices!, 9/13/2011 7:47:57 PM -07:00

Springboard Series Blog
     What to Expect in User Experience Virtualization Beta 2, 6/25/2012 11:03:27 PM -07:00
     Introducing Microsoft BitLocker Administration 2.0 Beta, 6/12/2012 8:08:23 AM -07:00
     . . .
     The Springboard Series Visits Lima, Peru, 11/18/2011 5:27:37 AM -08:00

The remainder of this topic provides details about how to create the example and how it works.

You must have Visual Studio 2012 and Windows 8 installed on your computer to run this app.

This topic contains the following sections.

The example is based on the blog reader that's described in Quickstart: using the await operator for asynchronous programming. However, the starter code for this topic downloads multiple blog feeds instead of just one.

The starter code uses Windows Runtime functionality to download the blog feeds sequentially. That is, the blog feeds are downloaded in the order in which they are listed in a collection of URLs. The finished app adds functionality from the .NET Framework to download the blog feeds in the order in which they complete.

You can set up the example code in any of the following ways:

The Understanding the Starter Code section discusses key points in the basic solution.

The Extending the Starter Code section shows you how to modify the code by adding AsTask and Task.WhenAny.

The starter code uses a SyndicationClient method, RetrieveFeedAsync, to download a blog feed from each URI in a list of URIs. Each call to the method returns an IAsyncOperationWithProgress instance that represents an ongoing asynchronous operation. When awaited, the asynchronous operation produces a SyndicationFeed instance that contains information about the downloaded blog feed.

The code defines a query that applies RetrieveFeedAsync to each entry in a list of URIs. When executed, the query returns a collection of IAsyncOperationWithProgress instances.

IEnumerable<IAsyncOperationWithProgress<SyndicationFeed, 
    RetrievalProgress>> feedsQuery = from uri in uriList
                                     select client.RetrieveFeedAsync(uri);

ToList<TSource> runs the query and starts the asynchronous processes, as the following code shows.

List<IAsyncOperationWithProgress<SyndicationFeed, 
    RetrievalProgress>> blogFeedOpsList = feedsQuery.ToList();

At this point, you have a list of active IAsyncOperationWithProgress instances. You still must await each instance to get the final results.

The following loop awaits each IAsyncOperationWithProgress instance to retrieve the SyndicationFeed results.

SyndicationFeed feed;
foreach (var blogFeedOp in blogFeedOpsList)
{
    // The await operator retrieves the final result (a SyndicationFeed instance) 
    // from each IAsyncOperation instance.
    feed = await blogFeedOp;
    DisplayResults(feed);
}

You can review this version of the program in the Building the Starter Code section at the end of the topic.

You can find more information about programming with asynchronous Windows Runtime APIs in Quickstart: using the await operator for asynchronous programming.

The starter code demonstrates that SyndicationClient makes it easy to download blog feeds. The remaining step to complete the example is to enable the application to process the blog feeds in the order in which their downloads complete instead of the order in which they appear in the list of URIs.

The key to accomplishing the enhancement is the Task.WhenAny method. When you apply WhenAny to a collection of asynchronous processes, the method returns the first process that completes, minimizing the time that you must wait. In this example, the order in which the blog feed information appears isn't important. If one download is slow, the results from another blog can display first. The situation seems perfect for WhenAny except for one thing: WhenAny requires a collection of tasks.

WhenAny requires a collection of Task or Task<TResult> instances, but the SyndicationClient method that downloads the blog feeds returns an IAsyncOperationWithProgress instance. Therefore, the app must bridge between the IAsyncOperationWithProgress objects from the Windows Runtime and Task objects from the .NET Framework.

The .NET Framework provides AsTask extension methods to make the transition. When you invoke AsTask on an IAsyncOperationWithProgress instance, AsTask returns a task that represents the asynchronous operation. The task completes when the corresponding IAsyncOperationWithProgress instance completes, and the task has the instance's result or exception.

Therefore, you just invoke AsTask on each IAsyncOperationWithProgress instance that RetrieveFeedAsync returns, as the following code shows. The code renames the variables to reflect the change to tasks and uses explicit typing for clarity.

IEnumerable<Task<SyndicationFeed>> feedsQuery =
        from uri in uriList
        // AsTask changes the returns from RetrieveFeedAsync into tasks. 
        select client.RetrieveFeedAsync(uri).AsTask();

// Run the query to start all the asynchronous processes.
List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();
Note Note

AsTask plays an important role in async programming that you probably aren't aware of. The compiler uses AsTask whenever you apply an await operator to an IAsyncAction or IAsyncOperation instance, as the following code shows.

  • ' What you write.
    Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(feedUri)
    
    ' What the compiler does.
    ' Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(feedUri).AsTask()
    
  • // What you write.     
    SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri); 
    
    // What the compiler does.  
    //SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri).AsTask();
    

The last step in the conversion is to add the Task.WhenAny method to the app. WhenAny is applied to a collection of tasks (blogFeedTasksList) and returns the first task in the collection that completes. More specifically, WhenAny returns a task that, when awaited, evaluates to the task that finished first.

The following statement calls WhenAny and awaits the result. The code uses explicit typing to show the result more clearly.

Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);

The following code does the same thing as the previous statement but breaks the operation into two statements to clarify what happens. The first statement calls WhenAny, and the second statement awaits the result.

// WhenAny returns a task that, when awaited, produces a task.
// Call:
Task<Task<SyndicationFeed>> whenAnyTask = Task.WhenAny(blogFeedTasksList);
// Await:
Task<SyndicationFeed> nextTask = await whenAnyTask;

Finally, you must await nextTask to retrieve the results (a SyndicationFeed instance) from the task that finished first, and then you must remove nextTask from the list so that you don't process it again.

feed = await nextTask;                    
blogFeedTasksList.Remove(nextTask);

Use a while loop to perform these steps for each task in blogFeedTasksList.

while (blogFeedTasksList.Count > 0)
{
    Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
    feed = await nextTask;                    
    blogFeedTasksList.Remove(nextTask);
    DisplayResults(feed);
}

You can review this version of the program in the Building the Finished App section at the end of the topic. Or you can follow the instructions in Downloading the Finished App to download the project.

Caution noteCaution

The use of WhenAny in a loop, as described in the example, is fine for problems that involve a small number of tasks. However, other approaches are more efficient if you have a large number of tasks to process. For more information and examples, see Processing Tasks as they complete.

You can download the starter code for the example from Async Sample: Bridging from .NET to Windows. If you don't have access to the Internet, follow the instructions in Building the Starter Code at the end of this topic to create the starter code.

After you download the code, you open and run it by performing the following steps.

  1. Decompress the file that you downloaded, and then start Visual Studio 2012.

  2. On the menu bar, choose File, Open, Project/Solution.

  3. Navigate to the folder that holds the decompressed sample code, and open the solution (.sln) file for AsTaskWhenAnyDemoVB or AsTaskWhenAnyDemoCS.

  4. In Solution Explorer, open the shortcut menu for the SequentialBlogReader project, and then choose Set as StartUp Project.

  5. Choose the F5 key to build and run the project.

  6. Run the code several times to verify that the results appear in the same order each time.

You can review the MainPage.xaml.vb or MainPage.xaml.cs file in the Building the Starter Code section at the end of the topic.

The example is based on the blog reader that's described in Quickstart: using the await operator for asynchronous programming. However, the starter code for this topic downloads multiple blog feeds instead of just one.

For information about a wide variety of improvements and extensions that you can make to the application, see Create a blog reader.

If you don't want to build the example yourself, you can download the complete example. Follow the instructions in the Downloading the Starter Code section, but choose WhenAnyBlogReader as the StartUp Project.

Run the program several times to verify that the blog feeds appear in different orders.

You can review the MainPage.xaml.vb or MainPage.xaml.cs file in the Building the Finished App section at the end of the topic.

You can download the examples in this topic from Async Sample: Bridging from .NET to Windows. If you prefer to set the application up yourself, follow these steps.

  1. Start Visual Studio 2012.

  2. On the menu bar, choose File, New, Project.

    The New Project dialog box opens.

  3. In the Installed, Templates category, choose Visual Basic or Visual C#, and then choose Windows Store in the list of project types.

  4. In the list of project types, choose Blank App (XAML).

  5. Name the project SequentialBlogReader, and then choose the OK button.

    The new project appears in Solution Explorer.

  6. In Solution Explorer, open the shortcut menu for MainPage.xaml, and then choose Open.

  7. In the XAML window of MainPage.xaml, replace the code with the following code.

    <Page
        x:Class="SequentialBlogReader.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AsTaskWhenAnyDemo"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Stretch" Margin="325,128,330,0" VerticalAlignment="Top" Click="StartButton_Click" Height="71" Background="#FFA89B9B" FontWeight="Bold" FontSize="36"/>
            <TextBox x:Name="ResultsTextBox" Margin="325,222,330,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Page>
    

    A simple window that contains a text box and a button appears in the Design window of MainPage.xaml.

    For information about a wide variety of improvements and extensions that you can make to the UI, see Create a blog reader.

  8. In Solution Explorer, open the shortcut menu for MainPage.xaml.vb or MainPage.xaml.cs, and then choose View Code.

  9. Replace the code in MainPage.xaml.vb or MainPage.xaml.cs with the following code.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // Add a using directive for SyndicationClient. 
    using Windows.Web.Syndication;
    
    
    namespace SequentialBlogReader
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
    
                // Disable the button until the operation is complete.
                StartButton.IsEnabled = false;
    
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                var uriList = CreateUriList();
    
                try
                {
                    IEnumerable<IAsyncOperationWithProgress<SyndicationFeed, 
                        RetrievalProgress>> feedsQuery = from uri in uriList
                                                         select client.RetrieveFeedAsync(uri);
    
                    // Run the query to start all the asynchronous processes.
                    List<IAsyncOperationWithProgress<SyndicationFeed, 
                        RetrievalProgress>> blogFeedOpsList = feedsQuery.ToList();
    
                    SyndicationFeed feed;
                    foreach (var blogFeedOp in blogFeedOpsList)
                    {
                        // The await operator retrieves the final result (a SyndicationFeed instance) 
                        // from each IAsyncOperation instance.
                        feed = await blogFeedOp;
                        DisplayResults(feed);
                    }
                }
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
                }
    
                // Reenable the button in case you want to run the operation again.
                StartButton.IsEnabled = true;
            }
    
            List<Uri> CreateUriList()
            {
                // Create a list of URIs.
                List<Uri> uriList = new List<Uri> 
                { 
                    new Uri("http://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    new Uri("http://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    new Uri("http://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    new Uri("http://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for blog posts. 
                foreach (SyndicationItem item in sf.Items)
                {
                    ResultsTextBox.Text += "\t" + item.Title.Text + ", " +
                                        item.PublishedDate.ToString() + "\r\n";
                }
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. Choose the F5 key to run the program, and then choose the Start button.

You can download the examples in this topic from Async Sample: Bridging from .NET to Windows. If you prefer to set the application up yourself, follow these steps.

  1. Start Visual Studio 2012.

  2. On the menu bar, choose File, New, Project.

    The New Project dialog box opens.

  3. In the Installed, Templates category, choose Visual Basic or Visual C#, and then choose Windows Store.

  4. From the list of project types, choose Blank App (XAML).

  5. Name the project WhenAnyBlogReader, and then choose the OK button.

    The new project appears in Solution Explorer.

  6. In Solution Explorer, open the shortcut menu for MainPage.xaml, and then choose Open.

  7. In the XAML window of MainPage.xaml, replace the code with the following code.

    <Page
        x:Class="WhenAnyBlogReader.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AsTaskWhenAnyDemo"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Stretch" Margin="325,128,330,0" VerticalAlignment="Top" Click="StartButton_Click" Height="71" Background="#FFA89B9B" FontWeight="Bold" FontSize="36"/>
            <TextBox x:Name="ResultsTextBox" Margin="325,222,330,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Page>
    

    A simple window that contains a text box and a button appears in the Design window of MainPage.xaml.

    For information about a wide variety of improvements and extensions that you can make to the application, see Create a blog reader.

  8. In Solution Explorer, open the shortcut menu for MainPage.xaml.vb or MainPage.xaml.cs, and then choose View Code.

  9. Replace the code in MainPage.xaml.vb or MainPage.xaml.cs with the following code.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // Add a using directive for SyndicationClient. 
    using Windows.Web.Syndication;
    
    // Add a using directive for the Tasks. 
    using System.Threading.Tasks;
    
    
    namespace WhenAnyBlogReader
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
    
                // Disable the button until the operation is complete.
                StartButton.IsEnabled = false;
    
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                var uriList = CreateUriList();
    
                // The following code avoids the use of implicit typing (var) so that you  
                // can identify the types clearly. 
    
                try
                {
                    IEnumerable<Task<SyndicationFeed>> feedsQuery =
                            from uri in uriList
                            // AsTask changes the returns from RetrieveFeedAsync into tasks. 
                            select client.RetrieveFeedAsync(uri).AsTask();
    
                    // Run the query to start all the asynchronous processes.
                    List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();
    
                    SyndicationFeed feed;
    
                    // Repeat the following until no tasks remain: 
                    //    - Grab the first one that finishes. 
                    //    - Retrieve the results from the task (what the return statement  
                    //      in RetrieveFeedAsync returns). 
                    //    - Remove the task from the list. 
                    //    - Display the results. 
                    while (blogFeedTasksList.Count > 0)
                    {
                        Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
                        feed = await nextTask;                    
                        blogFeedTasksList.Remove(nextTask);
                        DisplayResults(feed);
                    }
                }
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
                }
    
                // Reenable the button in case you want to run the operation again.
                StartButton.IsEnabled = true;
            }
    
    
            List<Uri> CreateUriList()
            {
                // Create a list of URIs.
                List<Uri> uriList = new List<Uri> 
                { 
                    new Uri("http://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    new Uri("http://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    new Uri("http://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    new Uri("http://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for blog posts. 
                foreach (SyndicationItem item in sf.Items)
                {
                    ResultsTextBox.Text += "\t" + item.Title.Text + ", " +
                                        item.PublishedDate.ToString() + "\r\n";
                }
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. Choose the F5 key to run the program, and then choose the Start button.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.