取消:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic)

您可以通过将 .NET framework 和 Windows 运行时的功能最大化您的资源。本主题中的示例演示如何使用 .NET FrameworkCancellationToken 的实例添加取消按钮添加到使用一个 Windows 运行时 方法下载从 web 的博客源 app 的。

说明说明

若要运行此示例,必须在计算机上安装 8 的窗口。此外,因此,如果要从 Visual Studio 运行测试的示例,还必须拥有 Visual Studio 2012 或 Visual Studio express 2012 中的 windows 8 安装。

AsTask 提供桥梁

取消标记要求 Task 实例,但是,Windows 运行时 方法产生 IAsyncOperationWithProgress 实例。在 .NET framework 可以使用 AsTask 扩展方法它们在它们之间。

在该示例中的 DownloadBlogsAsync 方法完成大部分工作。

Async Function DownloadBlogsAsync(ct As CancellationToken) As Task
    Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()

    Dim uriList = CreateUriList()

    ' Force the SyndicationClient to download the information.
    client.BypassCacheOnRetrieve = True


    ' The following code avoids the use of implicit typing (var) so that you 
    ' can identify the types clearly.

    For Each uri In uriList
        ' ***These three lines are combined in the single statement that follows them.
        'Dim feedOp As IAsyncOperationWithProgress(Of SyndicationFeed, RetrievalProgress) =
        '    client.RetrieveFeedAsync(uri)
        'Dim feedTask As Task(Of SyndicationFeed) = feedOp.AsTask(ct)
        'Dim feed As SyndicationFeed = Await feedTask

        ' ***You can combine the previous three steps in one expression.
        Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(uri).AsTask(ct)

        DisplayResults(feed, ct)
    Next
End Function
async Task DownloadBlogsAsync(CancellationToken ct)
{
    Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();

    var uriList = CreateUriList();

    // Force the SyndicationClient to download the information.
    client.BypassCacheOnRetrieve = true;

    // The following code avoids the use of implicit typing (var) so that you 
    // can identify the types clearly.

    foreach (var uri in uriList)
    {
        // ***These three lines are combined in the single statement that follows them.
        //IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> feedOp = 
        //    client.RetrieveFeedAsync(uri);
        //Task<SyndicationFeed> feedTask = feedOp.AsTask(ct);
        //SyndicationFeed feed = await feedTask;

        // ***You can combine the previous three steps in one expression.
        SyndicationFeed feed = await client.RetrieveFeedAsync(uri).AsTask(ct);

        DisplayResults(feed);
    }
}

在循环的注释部分的详细显示转换步骤。

  • SyndicationClient.RetrieveFeedAsync 元素的调用下载从指定的 URI 的博客源的异步操作。该异步操作是实例 IAsyncOperationWithProgress

    Dim feedOp As IAsyncOperationWithProgress(Of SyndicationFeed, RetrievalProgress) = 
        client.RetrieveFeedAsync(uri)
    
    IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> feedOp =  
        client.RetrieveFeedAsync(uri);
    
  • 由于在要使用的 .NET framework 的取消功能要求任务,代码将 AsTask 表示 IAsyncOperationWithProgress 实例作为 Task<TResult>。具体而言,代码将接受 CancellationToken 参数的 AsTask 超负载。

    Dim feedTask As Task(Of SyndicationFeed) = feedOp.AsTask(ct)
    
    Task<SyndicationFeed> feedTask = feedOp.AsTask(ct);
    
  • 最后,await 或 Await 运算符等待任务检索结果 SyndicationFeed

    Dim feed As SyndicationFeed = Await feedTask
    
    SyndicationFeed feed = await feedTask;
    

有关 AsTask的更多信息,请参见。WhenAny:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic)扩展起始代码

指向相关

可以查看整个示例通过移动到本主题的末尾,通过下载该示例到本地计算机,或通过生成该示例。有关更多信息和命令,请参见 设置示例

因为您查看此示例,将显示焦点的通知星号。建议您阅读本节更好地了解这些点,特别是,如果您以前没有使用 CancellationToken。

若要实现取消按钮,您的代码必须包括以下元素。

  • CancellationTokenSource 变量,cts,在任何方法的范围访问该元素

    Public NotInheritable Class MainPage
        Inherits Page
    
        ' ***Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
    
    public sealed partial class MainPage : Page
    {
        // ***Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;
    
  • 取消 按钮的事件处理程序。事件处理程序使用 CancellationTokenSource.Cancel 方法通知 cts,当用户请求取消。

    ' ***Add an event handler for the Cancel button.
    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)
        If cts IsNot Nothing Then
            cts.Cancel()
            ResultsTextBox.Text &= vbCrLf & "Downloads canceled by the Cancel button."
        End If
    End Sub
    
    // ***Add an event handler for the Cancel button.
    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        if (cts != null)
        {
            cts.Cancel();
            ResultsTextBox.Text += "\r\nDownloads canceled by the Cancel button.";
        }
    }
    
  • 启动 按钮的,StartButton_Click一个事件处理程序,包括以下事件。

    • 事件处理程序实例化 CancellationTokenSource,cts。

      cts = New CancellationTokenSource()
      
      // ***Instantiate the CancellationTokenSource.
      cts = new CancellationTokenSource();
      
    • 在对 DownloadBlogsAsync的调用,下载博客源,代码发送 ctsCancellationTokenSource.Token 属性作为参数。如果取消请求,Token 属性传播消息。

      Await DownloadBlogsAsync(cts.Token)
      
      await DownloadBlogsAsync(cts.Token);
      
    • 为 DownloadBlogsAsync 的调用包含 catch 为 OperationCanceledException 块结果的 try-catch 语句放置,当选择 取消 按钮时。异步方法的调用方定义采用哪些操作。此示例显示消息。

      下面的代码演示完整的 try-catch 语句。

      Try
          ' ***Send a token to carry the message if cancellation is requested.
          Await DownloadBlogsAsync(cts.Token)
      
          ' ***Check for cancellations.
      Catch op As OperationCanceledException
          ' In practice, this catch block often is empty. It is used to absorb
          ' the exception,
          ResultsTextBox.Text &= vbCrLf & "Cancellation exception bubbles up to the caller."
      
          ' Check for other exceptions.
      Catch ex As Exception
          ResultsTextBox.Text =
              "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
      End Try
      
      try
      {
          // ***Send a token to carry the message if cancellation is requested.
          await DownloadBlogsAsync(cts.Token);
      }
      // ***Check for cancellations.
      catch (OperationCanceledException)
      {
          // In practice, this catch block often is empty. It is used to absorb
          // the exception,
          ResultsTextBox.Text += "\r\nCancellation exception bubbles up to the caller.";
      }
      // Check for other exceptions.
      catch (Exception ex)
      {
          ResultsTextBox.Text =
              "Page could not be loaded.\r\n" + "Exception: " + ex.ToString();
      }
      
  • 如上文所述本主题,DownloadBlogsAsync 方法调用 Windows 运行时 方法,RetrieveFeedAsync,并将 .NET Framework 扩展方法,AsTask,在返回的 IAsyncOperation 实例。AsTask 表示实例作为 Task,因此,您可以发送取消标记传递到该异步操作。当选择 取消 按钮时,该标记传播消息。

    请注意,使用 AsTask,代码可以传递同一 CancellationToken 实例。Windows 运行时 方法 (RetrieveFeedAsync) 和 .NET Framework 方法 (DownloadBlogsAsync)。

    以下各行显示代码的这一部分。

    Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(uri).AsTask(ct)
    
    SyndicationFeed feed = await client.RetrieveFeedAsync(uri).AsTask(ct);
    
  • 如果不移除该应用程序,它产生下面的输出。

    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
        Windows Restart and Recovery Recipe, 3/21/2011 2:13:24 PM -07:00
    
    Extreme Windows Blog
        Samsung Series 9 27” PLS Display: Amazing Picture, 8/20/2012 2:41:48 PM -07:00
        NVIDIA GeForce GTX 660 Ti Graphics Card: Affordable Graphics Powerhouse, 8/16/2012 10:56:19 AM -07:00
        HP Z820 Workstation: Rising To the Challenge, 8/14/2012 1:57:01 PM -07:00
    
    Blogging Windows
        Windows Upgrade Offer Registration Now Available, 8/20/2012 1:01:00 PM -07:00
        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
    
    Windows for your Business
        What Windows 8 RTM Means for Businesses, 8/1/2012 9:01:00 AM -07:00
        Higher-Ed Learning with Windows 8, 7/26/2012 12:03:00 AM -07:00
        Second Public Beta of App-V 5.0 Now Available with Office Integration, 7/24/2012 10:07:26 AM -07:00
    
    Windows Experience Blog
        Tech Tuesday Live Twitter Chat with Microsoft Hardware, 8/20/2012 2:20:57 AM -07:00
        New Colors and New Artist Series Mice from Microsoft Hardware, 8/15/2012 12:06:35 AM -07:00
        Tech Tuesday Live Twitter Chat with HP on Keeping Kids Safe as They Head Back to School #winchat, 8/13/2012 12:24:18 PM -07:00
    
    Windows Security Blog
        Dealing with Fake Tech Support & Phone Scams, 6/16/2011 1:53:00 PM -07:00
        Combating social engineering tactics, like cookiejacking, to stay safer online, 5/28/2011 12:02:26 PM -07:00
        Windows 7 is now Common Criteria Certified!, 4/27/2011 9:35:01 AM -07:00
    
    Windows Home Server Blog
        Connecting Windows 8 Consumer Preview with Windows Home Server, 3/25/2012 9:06:00 AM -07:00
        Viridian PC Systems announces two new server models are available to order, 10/3/2011 12:36:00 PM -07:00
        PC Specialist to release Windows Home Server 2011, 9/27/2011 10:27:37 AM -07:00
    
    Springboard Series Blog
        Windows 8 Is Ready For Your Enterprise, 8/16/2012 9:59:00 AM -07:00
        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
    

    如果选择 取消 按钮,该 app 完成下载该内容之前,则结果将类似于下面的输出。

    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
        Windows Restart and Recovery Recipe, 3/21/2011 2:13:24 PM -07:00
    
    Extreme Windows Blog
        Samsung Series 9 27” PLS Display: Amazing Picture, 8/20/2012 2:41:48 PM -07:00
        NVIDIA GeForce GTX 660 Ti Graphics Card: Affordable Graphics Powerhouse, 8/16/2012 10:56:19 AM -07:00
        HP Z820 Workstation: Rising To the Challenge, 8/14/2012 1:57:01 PM -07:00
    
    Blogging Windows
        Windows Upgrade Offer Registration Now Available, 8/20/2012 1:01:00 PM -07:00
        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
    
    Windows for your Business
        What Windows 8 RTM Means for Businesses, 8/1/2012 9:01:00 AM -07:00
        Higher-Ed Learning with Windows 8, 7/26/2012 12:03:00 AM -07:00
        Second Public Beta of App-V 5.0 Now Available with Office Integration, 7/24/2012 10:07:26 AM -07:00
    
    Downloads canceled by the Cancel button.
    Cancellation exception bubbles up to the caller.
    

设置示例

可以下载该应用程序,生成该 hello 或检查代码本主题结尾,而无需实现它。必须在计算机上安装 Visual Studio 2012 和 windows 8 上运行此应用程序。

下载完毕的 app

  1. 下载压缩文件 Async 示例:它们在 .NET 和窗口运行时 (AsTask &取消)

  2. 对要下载的文件,然后启动 Visual Studio。

  3. 在菜单栏上,依次选择**“文件”“打开”“项目/解决方案”**。

  4. 导航到保存进行解压缩的代码示例的文件夹,然后打开解决方案文件 (.sln)。

  5. 选择 F5 键生成和运行项目。

    运行代码多次验证可以撤消在不同点。

生成完成的 app

  1. 启动 Visual Studio。

  2. 在菜单栏上,选择**“文件”“新建**、“项目”

    将打开**“新建项目”**对话框。

  3. 已安装模板 类别中,选择 Visual BasicVisual C#,然后选择 Windows 应用商店

  4. 从项目类型列表中,选择 空白应用程序 (XAML)

  5. 将项目命名为 BlogFeedWithCancellation,然后选择 确定 按钮。

    新项目出现在**“解决方案资源管理器”**中。

  6. 解决方案资源管理器,打开 MainPage.xaml 的快捷菜单,然后选择 打开

  7. XAML 窗口 MainPage.xaml 中,用下面的代码替换代码。

    <Page
        x:Class="BlogFeedWithCancellation.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:BlogFeedWithCancellation"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="325,77,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="145" Background="#FFA89B9B" FontSize="36" Width="355"  />
            <Button x:Name="CancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="684,77,0,0" VerticalAlignment="Top" Height="145" Background="#FFA89B9B" Click="CancelButton_Click" FontSize="36" Width="355"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="325,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="711" />
        </Grid>
    </Page>
    

    包含文本框简单的 windows,切换到"和"取消"按钮出现在 设计 窗口 MainPage.xaml。

  8. 解决方案资源管理器,打开 MainPage.xaml.vb 或 MainPage.xaml.cs 的快捷菜单,然后选择 查看代码

  9. 用下面的代码替换该 MainPage.xaml.vb 或 MainPage.xaml.cs 的代码。

    ' Add an Imports statement for SyndicationClient.
    Imports Windows.Web.Syndication
    ' Add an Imports statement for Tasks.
    Imports System.Threading.Tasks
    ' Add an Imports statement for CancellationToken.
    Imports System.Threading
    
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        ' ***Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ResultsTextBox.Text = ""
            ' Prevent unexpected reentrance.
            StartButton.IsEnabled = False
    
            ' ***Instantiate the CancellationTokenSource.
            cts = New CancellationTokenSource()
    
            Try
                ' ***Send a token to carry the message if cancellation is requested.
                Await DownloadBlogsAsync(cts.Token)
    
                ' ***Check for cancellations.
            Catch op As OperationCanceledException
                ' In practice, this catch block often is empty. It is used to absorb
                ' the exception,
                ResultsTextBox.Text &= vbCrLf & "Cancellation exception bubbles up to the caller."
    
                ' Check for other exceptions.
            Catch ex As Exception
                ResultsTextBox.Text =
                    "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
            End Try
    
            ' ***Set the CancellationTokenSource to null when the work is complete.
            cts = Nothing
    
            ' In case you want to try again.
            StartButton.IsEnabled = True
        End Sub
    
    
        ' Provide a parameter for the CancellationToken.
        Async Function DownloadBlogsAsync(ct As CancellationToken) As Task
            Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()
    
            Dim uriList = CreateUriList()
    
            ' Force the SyndicationClient to download the information.
            client.BypassCacheOnRetrieve = True
    
    
            ' The following code avoids the use of implicit typing (var) so that you 
            ' can identify the types clearly.
    
            For Each uri In uriList
                ' ***These three lines are combined in the single statement that follows them.
                'Dim feedOp As IAsyncOperationWithProgress(Of SyndicationFeed, RetrievalProgress) =
                '    client.RetrieveFeedAsync(uri)
                'Dim feedTask As Task(Of SyndicationFeed) = feedOp.AsTask(ct)
                'Dim feed As SyndicationFeed = Await feedTask
    
                ' ***You can combine the previous three steps in one expression.
                Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(uri).AsTask(ct)
    
                DisplayResults(feed, ct)
            Next
        End Function
    
    
        ' ***Add an event handler for the Cancel button.
        Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)
            If cts IsNot Nothing Then
                cts.Cancel()
                ResultsTextBox.Text &= vbCrLf & "Downloads canceled by the Cancel button."
            End If
        End Sub
    
    
        Function CreateUriList() As List(Of Uri)
            ' Create a list of URIs.
            Dim uriList = New List(Of Uri) From
                    {
                        New Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/business/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/windowssecurity/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx"),
                        New Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                    }
            Return uriList
        End Function
    
    
        ' You can pass the CancellationToken to this method if you think you might use a
        ' cancellable API here in the future.
        Sub DisplayResults(sf As SyndicationFeed, ct As CancellationToken)
            ' Title of the blog.
            ResultsTextBox.Text &= sf.Title.Text & vbCrLf
    
            ' Titles and dates for the first three blog posts.
            For i As Integer = 0 To If(sf.Items.Count >= 3, 2, sf.Items.Count)
                ResultsTextBox.Text &= vbTab & sf.Items.ElementAt(i).Title.Text & ", " &
                        sf.Items.ElementAt(i).PublishedDate.ToString() & vbCrLf
            Next
    
            ResultsTextBox.Text &= vbCrLf
        End Sub
    End Class
    
    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 Tasks.
    using System.Threading.Tasks;
    // Add a using directive for CancellationToken.
    using System.Threading;
    
    
    namespace BlogFeedWithCancellation
    {
        public sealed partial class MainPage : Page
        {
            // ***Declare a System.Threading.CancellationTokenSource.
            CancellationTokenSource cts;
    
            public MainPage()
            {
                this.InitializeComponent();
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
                // Prevent unexpected reentrance.
                StartButton.IsEnabled = false;
    
                // ***Instantiate the CancellationTokenSource.
                cts = new CancellationTokenSource();
    
                try
                {
                    // ***Send a token to carry the message if cancellation is requested.
                    await DownloadBlogsAsync(cts.Token);
                }
                // ***Check for cancellations.
                catch (OperationCanceledException)
                {
                    // In practice, this catch block often is empty. It is used to absorb
                    // the exception,
                    ResultsTextBox.Text += "\r\nCancellation exception bubbles up to the caller.";
                }
                // Check for other exceptions.
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\r\n" + "Exception: " + ex.ToString();
                }
    
                // ***Set the CancellationTokenSource to null when the work is complete.
                cts = null;
    
                // In case you want to try again.
                StartButton.IsEnabled = true;
            }
    
    
            // ***Provide a parameter for the CancellationToken.
            async Task DownloadBlogsAsync(CancellationToken ct)
            {
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                var uriList = CreateUriList();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                // The following code avoids the use of implicit typing (var) so that you 
                // can identify the types clearly.
    
                foreach (var uri in uriList)
                {
                    // ***These three lines are combined in the single statement that follows them.
                    //IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> feedOp = 
                    //    client.RetrieveFeedAsync(uri);
                    //Task<SyndicationFeed> feedTask = feedOp.AsTask(ct);
                    //SyndicationFeed feed = await feedTask;
    
                    // ***You can combine the previous three steps in one expression.
                    SyndicationFeed feed = await client.RetrieveFeedAsync(uri).AsTask(ct);
    
                    DisplayResults(feed);
                }
            }
    
    
    
            // ***Add an event handler for the Cancel button.
            private void CancelButton_Click(object sender, RoutedEventArgs e)
            {
                if (cts != null)
                {
                    cts.Cancel();
                    ResultsTextBox.Text += "\r\nDownloads canceled by the Cancel button.";
                }
            }
    
    
            List<Uri> CreateUriList()
            {
                // Create a list of URIs.
                List<Uri> uriList = new List<Uri> 
                { 
                    new Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/business/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/windowssecurity/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            // You can pass the CancellationToken to this method if you think you might use a
            // cancellable API here in the future.
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for the first three blog posts.
                for (int i = 0; i < (sf.Items.Count < 3 ? sf.Items.Count : 3); i++)    // Is Math.Min better?
                {
                    ResultsTextBox.Text += "\t" + sf.Items.ElementAt(i).Title.Text + ", " +
                        sf.Items.ElementAt(i).PublishedDate.ToString() + "\r\n";
                }
    
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. 选择 F5 键运行程序,然后选择 启动 按钮。

请参见

概念

WhenAny:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic)

取消一个任务或一组任务(C# 和 Visual Basic)

在一段时间后取消任务(C# 和 Visual Basic)

在完成一个任务后取消剩余任务(C# 和 Visual Basic)

取消标记