C#idiomatic C#
how-to-cancel-a-dataflow-blockstandard/parallel-programming

Async and concurrency

How to: Cancel a Dataflow Block

Learn more about: How to: Cancel a Dataflow Block

  • page standard/parallel-programming/how-to-cancel-a-dataflow-block
  • updated 03/30/2017

This document demonstrates how to enable cancellation in your application. This example uses Windows Forms to show where work items are active in a dataflow pipeline and also the effects of cancellation.

To Create the Windows Forms Application

  1. Create a C# or Visual Basic Windows Forms Application project. In the following steps, the project is named CancellationWinForms.

  2. On the form designer for the main form, Form1.cs (Form1.vb for Visual Basic), add a System.Windows.Forms.ToolStrip control.

  3. Add a System.Windows.Forms.ToolStripButton control to the System.Windows.Forms.ToolStrip control. Set the System.Windows.Forms.ToolStripItem.DisplayStyle property to System.Windows.Forms.ToolStripItemDisplayStyle.Text and the System.Windows.Forms.ToolStripItem.Text property to Add Work Items.

  4. Add a second System.Windows.Forms.ToolStripButton control to the System.Windows.Forms.ToolStrip control. Set the System.Windows.Forms.ToolStripItem.DisplayStyle property to System.Windows.Forms.ToolStripItemDisplayStyle.Text, the System.Windows.Forms.ToolStripItem.Text property to Cancel, and the System.Windows.Forms.ToolStripItem.Enabled property to False.

  5. Add four System.Windows.Forms.ToolStripProgressBar objects to the System.Windows.Forms.ToolStrip control.

Creating the Dataflow Pipeline

This section describes how to create the dataflow pipeline that processes work items and updates the progress bars.

To Create the Dataflow Pipeline

  1. In your project, add a reference to System.Threading.Tasks.Dataflow.dll.

  2. Ensure that Form1.cs (Form1.vb for Visual Basic) contains the following using directives (Imports in Visual Basic).

    sample 路 csharpsamples/snippets/csharp/VS_Snippets_Misc/tpldataflow_cancellationwinforms/cs/cancellationwinforms/form1.cs1

    csharp
    // <snippet100>
    // <snippet1>
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    // </snippet1>
    
    namespace CancellationWinForms
    {
       public partial class Form1 : Form
       {
          // <snippet2>
          // A placeholder type that performs work.
          class WorkItem
          {
             // Performs work for the provided number of milliseconds.
             public void DoWork(int milliseconds)
             {
                // For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds);
             }
          }
          // </snippet2>
    
          // <snippet3>
          // Enables the user interface to signal cancellation.
          CancellationTokenSource cancellationSource;
    
          // The first node in the dataflow pipeline.
          TransformBlock<WorkItem, WorkItem> startWork;
    
          // The second, and final, node in the dataflow pipeline.
          ActionBlock<WorkItem> completeWork;
    
          // Increments the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> incrementProgress;
    
          // Decrements the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> decrementProgress;
    
          // Enables progress bar actions to run on the UI thread.
          TaskScheduler uiTaskScheduler;
          // </snippet3>
    
          public Form1()
          {
             InitializeComponent();
    
             // Create the UI task scheduler from the current synchronization
             // context.
             uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
          }
    
          // <snippet4>
          // Creates the blocks that participate in the dataflow pipeline.
          private void CreatePipeline()
          {
             // Create the cancellation source.
             cancellationSource = new CancellationTokenSource();
    
             // Create the first node in the pipeline.
             startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(250);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar1);
    
                // Increment the progress bar that tracks the count of
                // active work items in the next stage of the pipeline.
                incrementProgress.Post(toolStripProgressBar2);
    
                // Send the work item to the next stage of the pipeline.
                return workItem;
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token
             });
    
             // Create the second, and final, node in the pipeline.
             completeWork = new ActionBlock<WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(1000);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar2);
    
                // Increment the progress bar that tracks the overall
                // count of completed work items.
                incrementProgress.Post(toolStripProgressBar3);
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token,
                MaxDegreeOfParallelism = 2
             });
    
             // Connect the two nodes of the pipeline. When the first node completes,
             // set the second node also to the completed state.
             startWork.LinkTo(
                completeWork, new DataflowLinkOptions { PropagateCompletion = true });
    
             // Create the dataflow action blocks that increment and decrement
             // progress bars.
             // These blocks use the task scheduler that is associated with
             // the UI thread.
    
             incrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value++,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
    
             decrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value--,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
          }
          // </snippet4>
    
          // <snippet5>
          // Event handler for the Add Work Items button.
          private void toolStripButton1_Click(object sender, EventArgs e)
          {
             // The Cancel button is disabled when the pipeline is not active.
             // Therefore, create the pipeline and enable the Cancel button
             // if the Cancel button is disabled.
             if (!toolStripButton2.Enabled)
             {
                CreatePipeline();
    
                // Enable the Cancel button.
                toolStripButton2.Enabled = true;
             }
    
             // Post several work items to the head of the pipeline.
             for (int i = 0; i < 5; i++)
             {
                toolStripProgressBar1.Value++;
                startWork.Post(new WorkItem());
             }
          }
          // </snippet5>
    
          // <snippet6>
          // Event handler for the Cancel button.
          private async void toolStripButton2_Click(object sender, EventArgs e)
          {
             // Disable both buttons.
             toolStripButton1.Enabled = false;
             toolStripButton2.Enabled = false;
    
             // Trigger cancellation.
             cancellationSource.Cancel();
    
             try
             {
                // Asynchronously wait for the pipeline to complete processing and for
                // the progress bars to update.
                await Task.WhenAll(
                   completeWork.Completion,
                   incrementProgress.Completion,
                   decrementProgress.Completion);
             }
             catch (OperationCanceledException)
             {
             }
    
             // Increment the progress bar that tracks the number of cancelled
             // work items by the number of active work items.
             toolStripProgressBar4.Value += toolStripProgressBar1.Value;
             toolStripProgressBar4.Value += toolStripProgressBar2.Value;
    
             // Reset the progress bars that track the number of active work items.
             toolStripProgressBar1.Value = 0;
             toolStripProgressBar2.Value = 0;
    
             // Enable the Add Work Items button.
             toolStripButton1.Enabled = true;
          }
          // </snippet6>
    
          ~Form1()
          {
             cancellationSource.Dispose();
          }
       }
    }
    // </snippet100>

    sample 路 vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms/form1.vb1

    vb
    ' <snippet100>
    ' <snippet1>
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System.Threading.Tasks.Dataflow
    
    ' </snippet1>
    
    Namespace CancellationWinForms
        Partial Public Class Form1
            Inherits Form
            ' <snippet2>
            ' A placeholder type that performs work.
            Private Class WorkItem
                ' Performs work for the provided number of milliseconds.
                Public Sub DoWork(ByVal milliseconds As Integer)
                    ' For demonstration, suspend the current thread.
                    Thread.Sleep(milliseconds)
                End Sub
            End Class
            ' </snippet2>
    
            ' <snippet3>
            ' Enables the user interface to signal cancellation.
            Private cancellationSource As CancellationTokenSource
    
            ' The first node in the dataflow pipeline.
            Private startWork As TransformBlock(Of WorkItem, WorkItem)
    
            ' The second, and final, node in the dataflow pipeline.
            Private completeWork As ActionBlock(Of WorkItem)
    
            ' Increments the value of the provided progress bar.
            Private incrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Decrements the value of the provided progress bar.
            Private decrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Enables progress bar actions to run on the UI thread.
            Private uiTaskScheduler As TaskScheduler
            ' </snippet3>
    
            Public Sub New()
                InitializeComponent()
    
                ' Create the UI task scheduler from the current synchronization
                ' context.
                uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            End Sub
    
            ' <snippet4>
            ' Creates the blocks that participate in the dataflow pipeline.
            Private Sub CreatePipeline()
                ' Create the cancellation source.
                cancellationSource = New CancellationTokenSource()
    
                ' Create the first node in the pipeline.
                startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                          ' Perform some work.
                                                                          ' Decrement the progress bar that tracks the count of
                                                                          ' active work items in this stage of the pipeline.
                                                                          ' Increment the progress bar that tracks the count of
                                                                          ' active work items in the next stage of the pipeline.
                                                                          ' Send the work item to the next stage of the pipeline.
                                                                          workItem.DoWork(250)
                                                                          decrementProgress.Post(toolStripProgressBar1)
                                                                          incrementProgress.Post(toolStripProgressBar2)
                                                                          Return workItem
                                                                      End Function,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
    
                ' Create the second, and final, node in the pipeline.
                completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                                ' Perform some work.
                                                                ' Decrement the progress bar that tracks the count of
                                                                ' active work items in this stage of the pipeline.
                                                                ' Increment the progress bar that tracks the overall
                                                                ' count of completed work items.
                                                                workItem.DoWork(1000)
                                                                decrementProgress.Post(toolStripProgressBar2)
                                                                incrementProgress.Post(toolStripProgressBar3)
                                                            End Sub,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                        .MaxDegreeOfParallelism = 2})
    
                ' Connect the two nodes of the pipeline. When the first node completes,
                ' set the second node also to the completed state.
                startWork.LinkTo(
                   completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
    
                ' Create the dataflow action blocks that increment and decrement
                ' progress bars.
                ' These blocks use the task scheduler that is associated with
                ' the UI thread.
    
                incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value += 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
                decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value -= 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
            End Sub
            ' </snippet4>
    
            ' <snippet5>
            ' Event handler for the Add Work Items button.
            Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
                ' The Cancel button is disabled when the pipeline is not active.
                ' Therefore, create the pipeline and enable the Cancel button
                ' if the Cancel button is disabled.
                If Not toolStripButton2.Enabled Then
                    CreatePipeline()
    
                    ' Enable the Cancel button.
                    toolStripButton2.Enabled = True
                End If
    
                ' Post several work items to the head of the pipeline.
                For i As Integer = 0 To 4
                    toolStripProgressBar1.Value += 1
                    startWork.Post(New WorkItem())
                Next i
            End Sub
            ' </snippet5>
    
            ' <snippet6>
            ' Event handler for the Cancel button.
            Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
                ' Disable both buttons.
                toolStripButton1.Enabled = False
                toolStripButton2.Enabled = False
    
                ' Trigger cancellation.
                cancellationSource.Cancel()
    
                Try
                    ' Asynchronously wait for the pipeline to complete processing and for
                    ' the progress bars to update.
                    Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
                Catch e1 As OperationCanceledException
                End Try
    
                ' Increment the progress bar that tracks the number of cancelled
                ' work items by the number of active work items.
                toolStripProgressBar4.Value += toolStripProgressBar1.Value
                toolStripProgressBar4.Value += toolStripProgressBar2.Value
    
                ' Reset the progress bars that track the number of active work items.
                toolStripProgressBar1.Value = 0
                toolStripProgressBar2.Value = 0
    
                ' Enable the Add Work Items button.
                toolStripButton1.Enabled = True
            End Sub
            ' </snippet6>
    
            Protected Overrides Sub Finalize()
                cancellationSource.Dispose()
                MyBase.Finalize()
            End Sub
        End Class
    End Namespace
    ' </snippet100>

  3. Add the WorkItem class as an inner type of the Form1 class.

    sample 路 csharpsamples/snippets/csharp/VS_Snippets_Misc/tpldataflow_cancellationwinforms/cs/cancellationwinforms/form1.cs2

    csharp
    // <snippet100>
    // <snippet1>
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    // </snippet1>
    
    namespace CancellationWinForms
    {
       public partial class Form1 : Form
       {
          // <snippet2>
          // A placeholder type that performs work.
          class WorkItem
          {
             // Performs work for the provided number of milliseconds.
             public void DoWork(int milliseconds)
             {
                // For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds);
             }
          }
          // </snippet2>
    
          // <snippet3>
          // Enables the user interface to signal cancellation.
          CancellationTokenSource cancellationSource;
    
          // The first node in the dataflow pipeline.
          TransformBlock<WorkItem, WorkItem> startWork;
    
          // The second, and final, node in the dataflow pipeline.
          ActionBlock<WorkItem> completeWork;
    
          // Increments the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> incrementProgress;
    
          // Decrements the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> decrementProgress;
    
          // Enables progress bar actions to run on the UI thread.
          TaskScheduler uiTaskScheduler;
          // </snippet3>
    
          public Form1()
          {
             InitializeComponent();
    
             // Create the UI task scheduler from the current synchronization
             // context.
             uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
          }
    
          // <snippet4>
          // Creates the blocks that participate in the dataflow pipeline.
          private void CreatePipeline()
          {
             // Create the cancellation source.
             cancellationSource = new CancellationTokenSource();
    
             // Create the first node in the pipeline.
             startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(250);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar1);
    
                // Increment the progress bar that tracks the count of
                // active work items in the next stage of the pipeline.
                incrementProgress.Post(toolStripProgressBar2);
    
                // Send the work item to the next stage of the pipeline.
                return workItem;
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token
             });
    
             // Create the second, and final, node in the pipeline.
             completeWork = new ActionBlock<WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(1000);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar2);
    
                // Increment the progress bar that tracks the overall
                // count of completed work items.
                incrementProgress.Post(toolStripProgressBar3);
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token,
                MaxDegreeOfParallelism = 2
             });
    
             // Connect the two nodes of the pipeline. When the first node completes,
             // set the second node also to the completed state.
             startWork.LinkTo(
                completeWork, new DataflowLinkOptions { PropagateCompletion = true });
    
             // Create the dataflow action blocks that increment and decrement
             // progress bars.
             // These blocks use the task scheduler that is associated with
             // the UI thread.
    
             incrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value++,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
    
             decrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value--,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
          }
          // </snippet4>
    
          // <snippet5>
          // Event handler for the Add Work Items button.
          private void toolStripButton1_Click(object sender, EventArgs e)
          {
             // The Cancel button is disabled when the pipeline is not active.
             // Therefore, create the pipeline and enable the Cancel button
             // if the Cancel button is disabled.
             if (!toolStripButton2.Enabled)
             {
                CreatePipeline();
    
                // Enable the Cancel button.
                toolStripButton2.Enabled = true;
             }
    
             // Post several work items to the head of the pipeline.
             for (int i = 0; i < 5; i++)
             {
                toolStripProgressBar1.Value++;
                startWork.Post(new WorkItem());
             }
          }
          // </snippet5>
    
          // <snippet6>
          // Event handler for the Cancel button.
          private async void toolStripButton2_Click(object sender, EventArgs e)
          {
             // Disable both buttons.
             toolStripButton1.Enabled = false;
             toolStripButton2.Enabled = false;
    
             // Trigger cancellation.
             cancellationSource.Cancel();
    
             try
             {
                // Asynchronously wait for the pipeline to complete processing and for
                // the progress bars to update.
                await Task.WhenAll(
                   completeWork.Completion,
                   incrementProgress.Completion,
                   decrementProgress.Completion);
             }
             catch (OperationCanceledException)
             {
             }
    
             // Increment the progress bar that tracks the number of cancelled
             // work items by the number of active work items.
             toolStripProgressBar4.Value += toolStripProgressBar1.Value;
             toolStripProgressBar4.Value += toolStripProgressBar2.Value;
    
             // Reset the progress bars that track the number of active work items.
             toolStripProgressBar1.Value = 0;
             toolStripProgressBar2.Value = 0;
    
             // Enable the Add Work Items button.
             toolStripButton1.Enabled = true;
          }
          // </snippet6>
    
          ~Form1()
          {
             cancellationSource.Dispose();
          }
       }
    }
    // </snippet100>

    sample 路 vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms/form1.vb2

    vb
    ' <snippet100>
    ' <snippet1>
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System.Threading.Tasks.Dataflow
    
    ' </snippet1>
    
    Namespace CancellationWinForms
        Partial Public Class Form1
            Inherits Form
            ' <snippet2>
            ' A placeholder type that performs work.
            Private Class WorkItem
                ' Performs work for the provided number of milliseconds.
                Public Sub DoWork(ByVal milliseconds As Integer)
                    ' For demonstration, suspend the current thread.
                    Thread.Sleep(milliseconds)
                End Sub
            End Class
            ' </snippet2>
    
            ' <snippet3>
            ' Enables the user interface to signal cancellation.
            Private cancellationSource As CancellationTokenSource
    
            ' The first node in the dataflow pipeline.
            Private startWork As TransformBlock(Of WorkItem, WorkItem)
    
            ' The second, and final, node in the dataflow pipeline.
            Private completeWork As ActionBlock(Of WorkItem)
    
            ' Increments the value of the provided progress bar.
            Private incrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Decrements the value of the provided progress bar.
            Private decrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Enables progress bar actions to run on the UI thread.
            Private uiTaskScheduler As TaskScheduler
            ' </snippet3>
    
            Public Sub New()
                InitializeComponent()
    
                ' Create the UI task scheduler from the current synchronization
                ' context.
                uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            End Sub
    
            ' <snippet4>
            ' Creates the blocks that participate in the dataflow pipeline.
            Private Sub CreatePipeline()
                ' Create the cancellation source.
                cancellationSource = New CancellationTokenSource()
    
                ' Create the first node in the pipeline.
                startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                          ' Perform some work.
                                                                          ' Decrement the progress bar that tracks the count of
                                                                          ' active work items in this stage of the pipeline.
                                                                          ' Increment the progress bar that tracks the count of
                                                                          ' active work items in the next stage of the pipeline.
                                                                          ' Send the work item to the next stage of the pipeline.
                                                                          workItem.DoWork(250)
                                                                          decrementProgress.Post(toolStripProgressBar1)
                                                                          incrementProgress.Post(toolStripProgressBar2)
                                                                          Return workItem
                                                                      End Function,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
    
                ' Create the second, and final, node in the pipeline.
                completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                                ' Perform some work.
                                                                ' Decrement the progress bar that tracks the count of
                                                                ' active work items in this stage of the pipeline.
                                                                ' Increment the progress bar that tracks the overall
                                                                ' count of completed work items.
                                                                workItem.DoWork(1000)
                                                                decrementProgress.Post(toolStripProgressBar2)
                                                                incrementProgress.Post(toolStripProgressBar3)
                                                            End Sub,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                        .MaxDegreeOfParallelism = 2})
    
                ' Connect the two nodes of the pipeline. When the first node completes,
                ' set the second node also to the completed state.
                startWork.LinkTo(
                   completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
    
                ' Create the dataflow action blocks that increment and decrement
                ' progress bars.
                ' These blocks use the task scheduler that is associated with
                ' the UI thread.
    
                incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value += 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
                decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value -= 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
            End Sub
            ' </snippet4>
    
            ' <snippet5>
            ' Event handler for the Add Work Items button.
            Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
                ' The Cancel button is disabled when the pipeline is not active.
                ' Therefore, create the pipeline and enable the Cancel button
                ' if the Cancel button is disabled.
                If Not toolStripButton2.Enabled Then
                    CreatePipeline()
    
                    ' Enable the Cancel button.
                    toolStripButton2.Enabled = True
                End If
    
                ' Post several work items to the head of the pipeline.
                For i As Integer = 0 To 4
                    toolStripProgressBar1.Value += 1
                    startWork.Post(New WorkItem())
                Next i
            End Sub
            ' </snippet5>
    
            ' <snippet6>
            ' Event handler for the Cancel button.
            Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
                ' Disable both buttons.
                toolStripButton1.Enabled = False
                toolStripButton2.Enabled = False
    
                ' Trigger cancellation.
                cancellationSource.Cancel()
    
                Try
                    ' Asynchronously wait for the pipeline to complete processing and for
                    ' the progress bars to update.
                    Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
                Catch e1 As OperationCanceledException
                End Try
    
                ' Increment the progress bar that tracks the number of cancelled
                ' work items by the number of active work items.
                toolStripProgressBar4.Value += toolStripProgressBar1.Value
                toolStripProgressBar4.Value += toolStripProgressBar2.Value
    
                ' Reset the progress bars that track the number of active work items.
                toolStripProgressBar1.Value = 0
                toolStripProgressBar2.Value = 0
    
                ' Enable the Add Work Items button.
                toolStripButton1.Enabled = True
            End Sub
            ' </snippet6>
    
            Protected Overrides Sub Finalize()
                cancellationSource.Dispose()
                MyBase.Finalize()
            End Sub
        End Class
    End Namespace
    ' </snippet100>

  4. Add the following data members to the Form1 class.

    sample 路 csharpsamples/snippets/csharp/VS_Snippets_Misc/tpldataflow_cancellationwinforms/cs/cancellationwinforms/form1.cs3

    csharp
    // <snippet100>
    // <snippet1>
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    // </snippet1>
    
    namespace CancellationWinForms
    {
       public partial class Form1 : Form
       {
          // <snippet2>
          // A placeholder type that performs work.
          class WorkItem
          {
             // Performs work for the provided number of milliseconds.
             public void DoWork(int milliseconds)
             {
                // For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds);
             }
          }
          // </snippet2>
    
          // <snippet3>
          // Enables the user interface to signal cancellation.
          CancellationTokenSource cancellationSource;
    
          // The first node in the dataflow pipeline.
          TransformBlock<WorkItem, WorkItem> startWork;
    
          // The second, and final, node in the dataflow pipeline.
          ActionBlock<WorkItem> completeWork;
    
          // Increments the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> incrementProgress;
    
          // Decrements the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> decrementProgress;
    
          // Enables progress bar actions to run on the UI thread.
          TaskScheduler uiTaskScheduler;
          // </snippet3>
    
          public Form1()
          {
             InitializeComponent();
    
             // Create the UI task scheduler from the current synchronization
             // context.
             uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
          }
    
          // <snippet4>
          // Creates the blocks that participate in the dataflow pipeline.
          private void CreatePipeline()
          {
             // Create the cancellation source.
             cancellationSource = new CancellationTokenSource();
    
             // Create the first node in the pipeline.
             startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(250);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar1);
    
                // Increment the progress bar that tracks the count of
                // active work items in the next stage of the pipeline.
                incrementProgress.Post(toolStripProgressBar2);
    
                // Send the work item to the next stage of the pipeline.
                return workItem;
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token
             });
    
             // Create the second, and final, node in the pipeline.
             completeWork = new ActionBlock<WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(1000);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar2);
    
                // Increment the progress bar that tracks the overall
                // count of completed work items.
                incrementProgress.Post(toolStripProgressBar3);
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token,
                MaxDegreeOfParallelism = 2
             });
    
             // Connect the two nodes of the pipeline. When the first node completes,
             // set the second node also to the completed state.
             startWork.LinkTo(
                completeWork, new DataflowLinkOptions { PropagateCompletion = true });
    
             // Create the dataflow action blocks that increment and decrement
             // progress bars.
             // These blocks use the task scheduler that is associated with
             // the UI thread.
    
             incrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value++,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
    
             decrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value--,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
          }
          // </snippet4>
    
          // <snippet5>
          // Event handler for the Add Work Items button.
          private void toolStripButton1_Click(object sender, EventArgs e)
          {
             // The Cancel button is disabled when the pipeline is not active.
             // Therefore, create the pipeline and enable the Cancel button
             // if the Cancel button is disabled.
             if (!toolStripButton2.Enabled)
             {
                CreatePipeline();
    
                // Enable the Cancel button.
                toolStripButton2.Enabled = true;
             }
    
             // Post several work items to the head of the pipeline.
             for (int i = 0; i < 5; i++)
             {
                toolStripProgressBar1.Value++;
                startWork.Post(new WorkItem());
             }
          }
          // </snippet5>
    
          // <snippet6>
          // Event handler for the Cancel button.
          private async void toolStripButton2_Click(object sender, EventArgs e)
          {
             // Disable both buttons.
             toolStripButton1.Enabled = false;
             toolStripButton2.Enabled = false;
    
             // Trigger cancellation.
             cancellationSource.Cancel();
    
             try
             {
                // Asynchronously wait for the pipeline to complete processing and for
                // the progress bars to update.
                await Task.WhenAll(
                   completeWork.Completion,
                   incrementProgress.Completion,
                   decrementProgress.Completion);
             }
             catch (OperationCanceledException)
             {
             }
    
             // Increment the progress bar that tracks the number of cancelled
             // work items by the number of active work items.
             toolStripProgressBar4.Value += toolStripProgressBar1.Value;
             toolStripProgressBar4.Value += toolStripProgressBar2.Value;
    
             // Reset the progress bars that track the number of active work items.
             toolStripProgressBar1.Value = 0;
             toolStripProgressBar2.Value = 0;
    
             // Enable the Add Work Items button.
             toolStripButton1.Enabled = true;
          }
          // </snippet6>
    
          ~Form1()
          {
             cancellationSource.Dispose();
          }
       }
    }
    // </snippet100>

    sample 路 vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms/form1.vb3

    vb
    ' <snippet100>
    ' <snippet1>
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System.Threading.Tasks.Dataflow
    
    ' </snippet1>
    
    Namespace CancellationWinForms
        Partial Public Class Form1
            Inherits Form
            ' <snippet2>
            ' A placeholder type that performs work.
            Private Class WorkItem
                ' Performs work for the provided number of milliseconds.
                Public Sub DoWork(ByVal milliseconds As Integer)
                    ' For demonstration, suspend the current thread.
                    Thread.Sleep(milliseconds)
                End Sub
            End Class
            ' </snippet2>
    
            ' <snippet3>
            ' Enables the user interface to signal cancellation.
            Private cancellationSource As CancellationTokenSource
    
            ' The first node in the dataflow pipeline.
            Private startWork As TransformBlock(Of WorkItem, WorkItem)
    
            ' The second, and final, node in the dataflow pipeline.
            Private completeWork As ActionBlock(Of WorkItem)
    
            ' Increments the value of the provided progress bar.
            Private incrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Decrements the value of the provided progress bar.
            Private decrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Enables progress bar actions to run on the UI thread.
            Private uiTaskScheduler As TaskScheduler
            ' </snippet3>
    
            Public Sub New()
                InitializeComponent()
    
                ' Create the UI task scheduler from the current synchronization
                ' context.
                uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            End Sub
    
            ' <snippet4>
            ' Creates the blocks that participate in the dataflow pipeline.
            Private Sub CreatePipeline()
                ' Create the cancellation source.
                cancellationSource = New CancellationTokenSource()
    
                ' Create the first node in the pipeline.
                startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                          ' Perform some work.
                                                                          ' Decrement the progress bar that tracks the count of
                                                                          ' active work items in this stage of the pipeline.
                                                                          ' Increment the progress bar that tracks the count of
                                                                          ' active work items in the next stage of the pipeline.
                                                                          ' Send the work item to the next stage of the pipeline.
                                                                          workItem.DoWork(250)
                                                                          decrementProgress.Post(toolStripProgressBar1)
                                                                          incrementProgress.Post(toolStripProgressBar2)
                                                                          Return workItem
                                                                      End Function,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
    
                ' Create the second, and final, node in the pipeline.
                completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                                ' Perform some work.
                                                                ' Decrement the progress bar that tracks the count of
                                                                ' active work items in this stage of the pipeline.
                                                                ' Increment the progress bar that tracks the overall
                                                                ' count of completed work items.
                                                                workItem.DoWork(1000)
                                                                decrementProgress.Post(toolStripProgressBar2)
                                                                incrementProgress.Post(toolStripProgressBar3)
                                                            End Sub,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                        .MaxDegreeOfParallelism = 2})
    
                ' Connect the two nodes of the pipeline. When the first node completes,
                ' set the second node also to the completed state.
                startWork.LinkTo(
                   completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
    
                ' Create the dataflow action blocks that increment and decrement
                ' progress bars.
                ' These blocks use the task scheduler that is associated with
                ' the UI thread.
    
                incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value += 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
                decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value -= 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
            End Sub
            ' </snippet4>
    
            ' <snippet5>
            ' Event handler for the Add Work Items button.
            Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
                ' The Cancel button is disabled when the pipeline is not active.
                ' Therefore, create the pipeline and enable the Cancel button
                ' if the Cancel button is disabled.
                If Not toolStripButton2.Enabled Then
                    CreatePipeline()
    
                    ' Enable the Cancel button.
                    toolStripButton2.Enabled = True
                End If
    
                ' Post several work items to the head of the pipeline.
                For i As Integer = 0 To 4
                    toolStripProgressBar1.Value += 1
                    startWork.Post(New WorkItem())
                Next i
            End Sub
            ' </snippet5>
    
            ' <snippet6>
            ' Event handler for the Cancel button.
            Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
                ' Disable both buttons.
                toolStripButton1.Enabled = False
                toolStripButton2.Enabled = False
    
                ' Trigger cancellation.
                cancellationSource.Cancel()
    
                Try
                    ' Asynchronously wait for the pipeline to complete processing and for
                    ' the progress bars to update.
                    Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
                Catch e1 As OperationCanceledException
                End Try
    
                ' Increment the progress bar that tracks the number of cancelled
                ' work items by the number of active work items.
                toolStripProgressBar4.Value += toolStripProgressBar1.Value
                toolStripProgressBar4.Value += toolStripProgressBar2.Value
    
                ' Reset the progress bars that track the number of active work items.
                toolStripProgressBar1.Value = 0
                toolStripProgressBar2.Value = 0
    
                ' Enable the Add Work Items button.
                toolStripButton1.Enabled = True
            End Sub
            ' </snippet6>
    
            Protected Overrides Sub Finalize()
                cancellationSource.Dispose()
                MyBase.Finalize()
            End Sub
        End Class
    End Namespace
    ' </snippet100>

  5. Add the following method, CreatePipeline, to the Form1 class.

    sample 路 csharpsamples/snippets/csharp/VS_Snippets_Misc/tpldataflow_cancellationwinforms/cs/cancellationwinforms/form1.cs4

    csharp
    // <snippet100>
    // <snippet1>
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    // </snippet1>
    
    namespace CancellationWinForms
    {
       public partial class Form1 : Form
       {
          // <snippet2>
          // A placeholder type that performs work.
          class WorkItem
          {
             // Performs work for the provided number of milliseconds.
             public void DoWork(int milliseconds)
             {
                // For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds);
             }
          }
          // </snippet2>
    
          // <snippet3>
          // Enables the user interface to signal cancellation.
          CancellationTokenSource cancellationSource;
    
          // The first node in the dataflow pipeline.
          TransformBlock<WorkItem, WorkItem> startWork;
    
          // The second, and final, node in the dataflow pipeline.
          ActionBlock<WorkItem> completeWork;
    
          // Increments the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> incrementProgress;
    
          // Decrements the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> decrementProgress;
    
          // Enables progress bar actions to run on the UI thread.
          TaskScheduler uiTaskScheduler;
          // </snippet3>
    
          public Form1()
          {
             InitializeComponent();
    
             // Create the UI task scheduler from the current synchronization
             // context.
             uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
          }
    
          // <snippet4>
          // Creates the blocks that participate in the dataflow pipeline.
          private void CreatePipeline()
          {
             // Create the cancellation source.
             cancellationSource = new CancellationTokenSource();
    
             // Create the first node in the pipeline.
             startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(250);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar1);
    
                // Increment the progress bar that tracks the count of
                // active work items in the next stage of the pipeline.
                incrementProgress.Post(toolStripProgressBar2);
    
                // Send the work item to the next stage of the pipeline.
                return workItem;
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token
             });
    
             // Create the second, and final, node in the pipeline.
             completeWork = new ActionBlock<WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(1000);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar2);
    
                // Increment the progress bar that tracks the overall
                // count of completed work items.
                incrementProgress.Post(toolStripProgressBar3);
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token,
                MaxDegreeOfParallelism = 2
             });
    
             // Connect the two nodes of the pipeline. When the first node completes,
             // set the second node also to the completed state.
             startWork.LinkTo(
                completeWork, new DataflowLinkOptions { PropagateCompletion = true });
    
             // Create the dataflow action blocks that increment and decrement
             // progress bars.
             // These blocks use the task scheduler that is associated with
             // the UI thread.
    
             incrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value++,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
    
             decrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value--,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
          }
          // </snippet4>
    
          // <snippet5>
          // Event handler for the Add Work Items button.
          private void toolStripButton1_Click(object sender, EventArgs e)
          {
             // The Cancel button is disabled when the pipeline is not active.
             // Therefore, create the pipeline and enable the Cancel button
             // if the Cancel button is disabled.
             if (!toolStripButton2.Enabled)
             {
                CreatePipeline();
    
                // Enable the Cancel button.
                toolStripButton2.Enabled = true;
             }
    
             // Post several work items to the head of the pipeline.
             for (int i = 0; i < 5; i++)
             {
                toolStripProgressBar1.Value++;
                startWork.Post(new WorkItem());
             }
          }
          // </snippet5>
    
          // <snippet6>
          // Event handler for the Cancel button.
          private async void toolStripButton2_Click(object sender, EventArgs e)
          {
             // Disable both buttons.
             toolStripButton1.Enabled = false;
             toolStripButton2.Enabled = false;
    
             // Trigger cancellation.
             cancellationSource.Cancel();
    
             try
             {
                // Asynchronously wait for the pipeline to complete processing and for
                // the progress bars to update.
                await Task.WhenAll(
                   completeWork.Completion,
                   incrementProgress.Completion,
                   decrementProgress.Completion);
             }
             catch (OperationCanceledException)
             {
             }
    
             // Increment the progress bar that tracks the number of cancelled
             // work items by the number of active work items.
             toolStripProgressBar4.Value += toolStripProgressBar1.Value;
             toolStripProgressBar4.Value += toolStripProgressBar2.Value;
    
             // Reset the progress bars that track the number of active work items.
             toolStripProgressBar1.Value = 0;
             toolStripProgressBar2.Value = 0;
    
             // Enable the Add Work Items button.
             toolStripButton1.Enabled = true;
          }
          // </snippet6>
    
          ~Form1()
          {
             cancellationSource.Dispose();
          }
       }
    }
    // </snippet100>

    sample 路 vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms/form1.vb4

    vb
    ' <snippet100>
    ' <snippet1>
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System.Threading.Tasks.Dataflow
    
    ' </snippet1>
    
    Namespace CancellationWinForms
        Partial Public Class Form1
            Inherits Form
            ' <snippet2>
            ' A placeholder type that performs work.
            Private Class WorkItem
                ' Performs work for the provided number of milliseconds.
                Public Sub DoWork(ByVal milliseconds As Integer)
                    ' For demonstration, suspend the current thread.
                    Thread.Sleep(milliseconds)
                End Sub
            End Class
            ' </snippet2>
    
            ' <snippet3>
            ' Enables the user interface to signal cancellation.
            Private cancellationSource As CancellationTokenSource
    
            ' The first node in the dataflow pipeline.
            Private startWork As TransformBlock(Of WorkItem, WorkItem)
    
            ' The second, and final, node in the dataflow pipeline.
            Private completeWork As ActionBlock(Of WorkItem)
    
            ' Increments the value of the provided progress bar.
            Private incrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Decrements the value of the provided progress bar.
            Private decrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Enables progress bar actions to run on the UI thread.
            Private uiTaskScheduler As TaskScheduler
            ' </snippet3>
    
            Public Sub New()
                InitializeComponent()
    
                ' Create the UI task scheduler from the current synchronization
                ' context.
                uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            End Sub
    
            ' <snippet4>
            ' Creates the blocks that participate in the dataflow pipeline.
            Private Sub CreatePipeline()
                ' Create the cancellation source.
                cancellationSource = New CancellationTokenSource()
    
                ' Create the first node in the pipeline.
                startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                          ' Perform some work.
                                                                          ' Decrement the progress bar that tracks the count of
                                                                          ' active work items in this stage of the pipeline.
                                                                          ' Increment the progress bar that tracks the count of
                                                                          ' active work items in the next stage of the pipeline.
                                                                          ' Send the work item to the next stage of the pipeline.
                                                                          workItem.DoWork(250)
                                                                          decrementProgress.Post(toolStripProgressBar1)
                                                                          incrementProgress.Post(toolStripProgressBar2)
                                                                          Return workItem
                                                                      End Function,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
    
                ' Create the second, and final, node in the pipeline.
                completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                                ' Perform some work.
                                                                ' Decrement the progress bar that tracks the count of
                                                                ' active work items in this stage of the pipeline.
                                                                ' Increment the progress bar that tracks the overall
                                                                ' count of completed work items.
                                                                workItem.DoWork(1000)
                                                                decrementProgress.Post(toolStripProgressBar2)
                                                                incrementProgress.Post(toolStripProgressBar3)
                                                            End Sub,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                        .MaxDegreeOfParallelism = 2})
    
                ' Connect the two nodes of the pipeline. When the first node completes,
                ' set the second node also to the completed state.
                startWork.LinkTo(
                   completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
    
                ' Create the dataflow action blocks that increment and decrement
                ' progress bars.
                ' These blocks use the task scheduler that is associated with
                ' the UI thread.
    
                incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value += 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
                decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value -= 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
            End Sub
            ' </snippet4>
    
            ' <snippet5>
            ' Event handler for the Add Work Items button.
            Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
                ' The Cancel button is disabled when the pipeline is not active.
                ' Therefore, create the pipeline and enable the Cancel button
                ' if the Cancel button is disabled.
                If Not toolStripButton2.Enabled Then
                    CreatePipeline()
    
                    ' Enable the Cancel button.
                    toolStripButton2.Enabled = True
                End If
    
                ' Post several work items to the head of the pipeline.
                For i As Integer = 0 To 4
                    toolStripProgressBar1.Value += 1
                    startWork.Post(New WorkItem())
                Next i
            End Sub
            ' </snippet5>
    
            ' <snippet6>
            ' Event handler for the Cancel button.
            Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
                ' Disable both buttons.
                toolStripButton1.Enabled = False
                toolStripButton2.Enabled = False
    
                ' Trigger cancellation.
                cancellationSource.Cancel()
    
                Try
                    ' Asynchronously wait for the pipeline to complete processing and for
                    ' the progress bars to update.
                    Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
                Catch e1 As OperationCanceledException
                End Try
    
                ' Increment the progress bar that tracks the number of cancelled
                ' work items by the number of active work items.
                toolStripProgressBar4.Value += toolStripProgressBar1.Value
                toolStripProgressBar4.Value += toolStripProgressBar2.Value
    
                ' Reset the progress bars that track the number of active work items.
                toolStripProgressBar1.Value = 0
                toolStripProgressBar2.Value = 0
    
                ' Enable the Add Work Items button.
                toolStripButton1.Enabled = True
            End Sub
            ' </snippet6>
    
            Protected Overrides Sub Finalize()
                cancellationSource.Dispose()
                MyBase.Finalize()
            End Sub
        End Class
    End Namespace
    ' </snippet100>

Because the incrementProgress and decrementProgress dataflow blocks act on the user interface, it is important that these actions occur on the user-interface thread. To accomplish this, during construction these objects each provide an System.Threading.Tasks.Dataflow.ExecutionDataflowBlockOptions object that has the System.Threading.Tasks.Dataflow.DataflowBlockOptions.TaskScheduler property set to System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext. The System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext method creates a System.Threading.Tasks.TaskScheduler object that performs work on the current synchronization context. Because the Form1 constructor is called from the user-interface thread, the actions for the incrementProgress and decrementProgress dataflow blocks also run on the user-interface thread.

This example sets the System.Threading.Tasks.Dataflow.DataflowBlockOptions.CancellationToken property when it constructs the members of the pipeline. Because the System.Threading.Tasks.Dataflow.DataflowBlockOptions.CancellationToken property permanently cancels dataflow block execution, the whole pipeline must be recreated after the user cancels the operation and then wants to add more work items to the pipeline. For an example that demonstrates an alternative way to cancel a dataflow block so that other work can be performed after an operation is canceled, see Walkthrough: Using Dataflow in a Windows Forms Application.

Connecting the Dataflow Pipeline to the User Interface

This section describes how to connect the dataflow pipeline to the user interface. Both creating the pipeline and adding work items to the pipeline are controlled by the event handler for the Add Work Items button. Cancellation is initiated by the Cancel button. When the user clicks either of these buttons, the appropriate action is initiated in an asynchronous manner.

To Connect the Dataflow Pipeline to the User Interface

  1. On the form designer for the main form, create an event handler for the System.Windows.Forms.ToolStripItem.Click event for the Add Work Items button.

  2. Implement the System.Windows.Forms.ToolStripItem.Click event for the Add Work Items button.

    sample 路 csharpsamples/snippets/csharp/VS_Snippets_Misc/tpldataflow_cancellationwinforms/cs/cancellationwinforms/form1.cs5

    csharp
    // <snippet100>
    // <snippet1>
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    // </snippet1>
    
    namespace CancellationWinForms
    {
       public partial class Form1 : Form
       {
          // <snippet2>
          // A placeholder type that performs work.
          class WorkItem
          {
             // Performs work for the provided number of milliseconds.
             public void DoWork(int milliseconds)
             {
                // For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds);
             }
          }
          // </snippet2>
    
          // <snippet3>
          // Enables the user interface to signal cancellation.
          CancellationTokenSource cancellationSource;
    
          // The first node in the dataflow pipeline.
          TransformBlock<WorkItem, WorkItem> startWork;
    
          // The second, and final, node in the dataflow pipeline.
          ActionBlock<WorkItem> completeWork;
    
          // Increments the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> incrementProgress;
    
          // Decrements the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> decrementProgress;
    
          // Enables progress bar actions to run on the UI thread.
          TaskScheduler uiTaskScheduler;
          // </snippet3>
    
          public Form1()
          {
             InitializeComponent();
    
             // Create the UI task scheduler from the current synchronization
             // context.
             uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
          }
    
          // <snippet4>
          // Creates the blocks that participate in the dataflow pipeline.
          private void CreatePipeline()
          {
             // Create the cancellation source.
             cancellationSource = new CancellationTokenSource();
    
             // Create the first node in the pipeline.
             startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(250);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar1);
    
                // Increment the progress bar that tracks the count of
                // active work items in the next stage of the pipeline.
                incrementProgress.Post(toolStripProgressBar2);
    
                // Send the work item to the next stage of the pipeline.
                return workItem;
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token
             });
    
             // Create the second, and final, node in the pipeline.
             completeWork = new ActionBlock<WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(1000);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar2);
    
                // Increment the progress bar that tracks the overall
                // count of completed work items.
                incrementProgress.Post(toolStripProgressBar3);
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token,
                MaxDegreeOfParallelism = 2
             });
    
             // Connect the two nodes of the pipeline. When the first node completes,
             // set the second node also to the completed state.
             startWork.LinkTo(
                completeWork, new DataflowLinkOptions { PropagateCompletion = true });
    
             // Create the dataflow action blocks that increment and decrement
             // progress bars.
             // These blocks use the task scheduler that is associated with
             // the UI thread.
    
             incrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value++,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
    
             decrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value--,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
          }
          // </snippet4>
    
          // <snippet5>
          // Event handler for the Add Work Items button.
          private void toolStripButton1_Click(object sender, EventArgs e)
          {
             // The Cancel button is disabled when the pipeline is not active.
             // Therefore, create the pipeline and enable the Cancel button
             // if the Cancel button is disabled.
             if (!toolStripButton2.Enabled)
             {
                CreatePipeline();
    
                // Enable the Cancel button.
                toolStripButton2.Enabled = true;
             }
    
             // Post several work items to the head of the pipeline.
             for (int i = 0; i < 5; i++)
             {
                toolStripProgressBar1.Value++;
                startWork.Post(new WorkItem());
             }
          }
          // </snippet5>
    
          // <snippet6>
          // Event handler for the Cancel button.
          private async void toolStripButton2_Click(object sender, EventArgs e)
          {
             // Disable both buttons.
             toolStripButton1.Enabled = false;
             toolStripButton2.Enabled = false;
    
             // Trigger cancellation.
             cancellationSource.Cancel();
    
             try
             {
                // Asynchronously wait for the pipeline to complete processing and for
                // the progress bars to update.
                await Task.WhenAll(
                   completeWork.Completion,
                   incrementProgress.Completion,
                   decrementProgress.Completion);
             }
             catch (OperationCanceledException)
             {
             }
    
             // Increment the progress bar that tracks the number of cancelled
             // work items by the number of active work items.
             toolStripProgressBar4.Value += toolStripProgressBar1.Value;
             toolStripProgressBar4.Value += toolStripProgressBar2.Value;
    
             // Reset the progress bars that track the number of active work items.
             toolStripProgressBar1.Value = 0;
             toolStripProgressBar2.Value = 0;
    
             // Enable the Add Work Items button.
             toolStripButton1.Enabled = true;
          }
          // </snippet6>
    
          ~Form1()
          {
             cancellationSource.Dispose();
          }
       }
    }
    // </snippet100>

    sample 路 vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms/form1.vb5

    vb
    ' <snippet100>
    ' <snippet1>
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System.Threading.Tasks.Dataflow
    
    ' </snippet1>
    
    Namespace CancellationWinForms
        Partial Public Class Form1
            Inherits Form
            ' <snippet2>
            ' A placeholder type that performs work.
            Private Class WorkItem
                ' Performs work for the provided number of milliseconds.
                Public Sub DoWork(ByVal milliseconds As Integer)
                    ' For demonstration, suspend the current thread.
                    Thread.Sleep(milliseconds)
                End Sub
            End Class
            ' </snippet2>
    
            ' <snippet3>
            ' Enables the user interface to signal cancellation.
            Private cancellationSource As CancellationTokenSource
    
            ' The first node in the dataflow pipeline.
            Private startWork As TransformBlock(Of WorkItem, WorkItem)
    
            ' The second, and final, node in the dataflow pipeline.
            Private completeWork As ActionBlock(Of WorkItem)
    
            ' Increments the value of the provided progress bar.
            Private incrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Decrements the value of the provided progress bar.
            Private decrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Enables progress bar actions to run on the UI thread.
            Private uiTaskScheduler As TaskScheduler
            ' </snippet3>
    
            Public Sub New()
                InitializeComponent()
    
                ' Create the UI task scheduler from the current synchronization
                ' context.
                uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            End Sub
    
            ' <snippet4>
            ' Creates the blocks that participate in the dataflow pipeline.
            Private Sub CreatePipeline()
                ' Create the cancellation source.
                cancellationSource = New CancellationTokenSource()
    
                ' Create the first node in the pipeline.
                startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                          ' Perform some work.
                                                                          ' Decrement the progress bar that tracks the count of
                                                                          ' active work items in this stage of the pipeline.
                                                                          ' Increment the progress bar that tracks the count of
                                                                          ' active work items in the next stage of the pipeline.
                                                                          ' Send the work item to the next stage of the pipeline.
                                                                          workItem.DoWork(250)
                                                                          decrementProgress.Post(toolStripProgressBar1)
                                                                          incrementProgress.Post(toolStripProgressBar2)
                                                                          Return workItem
                                                                      End Function,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
    
                ' Create the second, and final, node in the pipeline.
                completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                                ' Perform some work.
                                                                ' Decrement the progress bar that tracks the count of
                                                                ' active work items in this stage of the pipeline.
                                                                ' Increment the progress bar that tracks the overall
                                                                ' count of completed work items.
                                                                workItem.DoWork(1000)
                                                                decrementProgress.Post(toolStripProgressBar2)
                                                                incrementProgress.Post(toolStripProgressBar3)
                                                            End Sub,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                        .MaxDegreeOfParallelism = 2})
    
                ' Connect the two nodes of the pipeline. When the first node completes,
                ' set the second node also to the completed state.
                startWork.LinkTo(
                   completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
    
                ' Create the dataflow action blocks that increment and decrement
                ' progress bars.
                ' These blocks use the task scheduler that is associated with
                ' the UI thread.
    
                incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value += 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
                decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value -= 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
            End Sub
            ' </snippet4>
    
            ' <snippet5>
            ' Event handler for the Add Work Items button.
            Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
                ' The Cancel button is disabled when the pipeline is not active.
                ' Therefore, create the pipeline and enable the Cancel button
                ' if the Cancel button is disabled.
                If Not toolStripButton2.Enabled Then
                    CreatePipeline()
    
                    ' Enable the Cancel button.
                    toolStripButton2.Enabled = True
                End If
    
                ' Post several work items to the head of the pipeline.
                For i As Integer = 0 To 4
                    toolStripProgressBar1.Value += 1
                    startWork.Post(New WorkItem())
                Next i
            End Sub
            ' </snippet5>
    
            ' <snippet6>
            ' Event handler for the Cancel button.
            Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
                ' Disable both buttons.
                toolStripButton1.Enabled = False
                toolStripButton2.Enabled = False
    
                ' Trigger cancellation.
                cancellationSource.Cancel()
    
                Try
                    ' Asynchronously wait for the pipeline to complete processing and for
                    ' the progress bars to update.
                    Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
                Catch e1 As OperationCanceledException
                End Try
    
                ' Increment the progress bar that tracks the number of cancelled
                ' work items by the number of active work items.
                toolStripProgressBar4.Value += toolStripProgressBar1.Value
                toolStripProgressBar4.Value += toolStripProgressBar2.Value
    
                ' Reset the progress bars that track the number of active work items.
                toolStripProgressBar1.Value = 0
                toolStripProgressBar2.Value = 0
    
                ' Enable the Add Work Items button.
                toolStripButton1.Enabled = True
            End Sub
            ' </snippet6>
    
            Protected Overrides Sub Finalize()
                cancellationSource.Dispose()
                MyBase.Finalize()
            End Sub
        End Class
    End Namespace
    ' </snippet100>

  3. On the form designer for the main form, create an event handler for the System.Windows.Forms.ToolStripItem.Click event handler for the Cancel button.

  4. Implement the System.Windows.Forms.ToolStripItem.Click event handler for the Cancel button.

    sample 路 csharpsamples/snippets/csharp/VS_Snippets_Misc/tpldataflow_cancellationwinforms/cs/cancellationwinforms/form1.cs6

    csharp
    // <snippet100>
    // <snippet1>
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    // </snippet1>
    
    namespace CancellationWinForms
    {
       public partial class Form1 : Form
       {
          // <snippet2>
          // A placeholder type that performs work.
          class WorkItem
          {
             // Performs work for the provided number of milliseconds.
             public void DoWork(int milliseconds)
             {
                // For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds);
             }
          }
          // </snippet2>
    
          // <snippet3>
          // Enables the user interface to signal cancellation.
          CancellationTokenSource cancellationSource;
    
          // The first node in the dataflow pipeline.
          TransformBlock<WorkItem, WorkItem> startWork;
    
          // The second, and final, node in the dataflow pipeline.
          ActionBlock<WorkItem> completeWork;
    
          // Increments the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> incrementProgress;
    
          // Decrements the value of the provided progress bar.
          ActionBlock<ToolStripProgressBar> decrementProgress;
    
          // Enables progress bar actions to run on the UI thread.
          TaskScheduler uiTaskScheduler;
          // </snippet3>
    
          public Form1()
          {
             InitializeComponent();
    
             // Create the UI task scheduler from the current synchronization
             // context.
             uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
          }
    
          // <snippet4>
          // Creates the blocks that participate in the dataflow pipeline.
          private void CreatePipeline()
          {
             // Create the cancellation source.
             cancellationSource = new CancellationTokenSource();
    
             // Create the first node in the pipeline.
             startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(250);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar1);
    
                // Increment the progress bar that tracks the count of
                // active work items in the next stage of the pipeline.
                incrementProgress.Post(toolStripProgressBar2);
    
                // Send the work item to the next stage of the pipeline.
                return workItem;
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token
             });
    
             // Create the second, and final, node in the pipeline.
             completeWork = new ActionBlock<WorkItem>(workItem =>
             {
                // Perform some work.
                workItem.DoWork(1000);
    
                // Decrement the progress bar that tracks the count of
                // active work items in this stage of the pipeline.
                decrementProgress.Post(toolStripProgressBar2);
    
                // Increment the progress bar that tracks the overall
                // count of completed work items.
                incrementProgress.Post(toolStripProgressBar3);
             },
             new ExecutionDataflowBlockOptions
             {
                CancellationToken = cancellationSource.Token,
                MaxDegreeOfParallelism = 2
             });
    
             // Connect the two nodes of the pipeline. When the first node completes,
             // set the second node also to the completed state.
             startWork.LinkTo(
                completeWork, new DataflowLinkOptions { PropagateCompletion = true });
    
             // Create the dataflow action blocks that increment and decrement
             // progress bars.
             // These blocks use the task scheduler that is associated with
             // the UI thread.
    
             incrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value++,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
    
             decrementProgress = new ActionBlock<ToolStripProgressBar>(
                progressBar => progressBar.Value--,
                new ExecutionDataflowBlockOptions
                {
                   CancellationToken = cancellationSource.Token,
                   TaskScheduler = uiTaskScheduler
                });
          }
          // </snippet4>
    
          // <snippet5>
          // Event handler for the Add Work Items button.
          private void toolStripButton1_Click(object sender, EventArgs e)
          {
             // The Cancel button is disabled when the pipeline is not active.
             // Therefore, create the pipeline and enable the Cancel button
             // if the Cancel button is disabled.
             if (!toolStripButton2.Enabled)
             {
                CreatePipeline();
    
                // Enable the Cancel button.
                toolStripButton2.Enabled = true;
             }
    
             // Post several work items to the head of the pipeline.
             for (int i = 0; i < 5; i++)
             {
                toolStripProgressBar1.Value++;
                startWork.Post(new WorkItem());
             }
          }
          // </snippet5>
    
          // <snippet6>
          // Event handler for the Cancel button.
          private async void toolStripButton2_Click(object sender, EventArgs e)
          {
             // Disable both buttons.
             toolStripButton1.Enabled = false;
             toolStripButton2.Enabled = false;
    
             // Trigger cancellation.
             cancellationSource.Cancel();
    
             try
             {
                // Asynchronously wait for the pipeline to complete processing and for
                // the progress bars to update.
                await Task.WhenAll(
                   completeWork.Completion,
                   incrementProgress.Completion,
                   decrementProgress.Completion);
             }
             catch (OperationCanceledException)
             {
             }
    
             // Increment the progress bar that tracks the number of cancelled
             // work items by the number of active work items.
             toolStripProgressBar4.Value += toolStripProgressBar1.Value;
             toolStripProgressBar4.Value += toolStripProgressBar2.Value;
    
             // Reset the progress bars that track the number of active work items.
             toolStripProgressBar1.Value = 0;
             toolStripProgressBar2.Value = 0;
    
             // Enable the Add Work Items button.
             toolStripButton1.Enabled = true;
          }
          // </snippet6>
    
          ~Form1()
          {
             cancellationSource.Dispose();
          }
       }
    }
    // </snippet100>

    sample 路 vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms/form1.vb6

    vb
    ' <snippet100>
    ' <snippet1>
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System.Threading.Tasks.Dataflow
    
    ' </snippet1>
    
    Namespace CancellationWinForms
        Partial Public Class Form1
            Inherits Form
            ' <snippet2>
            ' A placeholder type that performs work.
            Private Class WorkItem
                ' Performs work for the provided number of milliseconds.
                Public Sub DoWork(ByVal milliseconds As Integer)
                    ' For demonstration, suspend the current thread.
                    Thread.Sleep(milliseconds)
                End Sub
            End Class
            ' </snippet2>
    
            ' <snippet3>
            ' Enables the user interface to signal cancellation.
            Private cancellationSource As CancellationTokenSource
    
            ' The first node in the dataflow pipeline.
            Private startWork As TransformBlock(Of WorkItem, WorkItem)
    
            ' The second, and final, node in the dataflow pipeline.
            Private completeWork As ActionBlock(Of WorkItem)
    
            ' Increments the value of the provided progress bar.
            Private incrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Decrements the value of the provided progress bar.
            Private decrementProgress As ActionBlock(Of ToolStripProgressBar)
    
            ' Enables progress bar actions to run on the UI thread.
            Private uiTaskScheduler As TaskScheduler
            ' </snippet3>
    
            Public Sub New()
                InitializeComponent()
    
                ' Create the UI task scheduler from the current synchronization
                ' context.
                uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
            End Sub
    
            ' <snippet4>
            ' Creates the blocks that participate in the dataflow pipeline.
            Private Sub CreatePipeline()
                ' Create the cancellation source.
                cancellationSource = New CancellationTokenSource()
    
                ' Create the first node in the pipeline.
                startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                          ' Perform some work.
                                                                          ' Decrement the progress bar that tracks the count of
                                                                          ' active work items in this stage of the pipeline.
                                                                          ' Increment the progress bar that tracks the count of
                                                                          ' active work items in the next stage of the pipeline.
                                                                          ' Send the work item to the next stage of the pipeline.
                                                                          workItem.DoWork(250)
                                                                          decrementProgress.Post(toolStripProgressBar1)
                                                                          incrementProgress.Post(toolStripProgressBar2)
                                                                          Return workItem
                                                                      End Function,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
    
                ' Create the second, and final, node in the pipeline.
                completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                                ' Perform some work.
                                                                ' Decrement the progress bar that tracks the count of
                                                                ' active work items in this stage of the pipeline.
                                                                ' Increment the progress bar that tracks the overall
                                                                ' count of completed work items.
                                                                workItem.DoWork(1000)
                                                                decrementProgress.Post(toolStripProgressBar2)
                                                                incrementProgress.Post(toolStripProgressBar3)
                                                            End Sub,
                New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                        .MaxDegreeOfParallelism = 2})
    
                ' Connect the two nodes of the pipeline. When the first node completes,
                ' set the second node also to the completed state.
                startWork.LinkTo(
                   completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
    
                ' Create the dataflow action blocks that increment and decrement
                ' progress bars.
                ' These blocks use the task scheduler that is associated with
                ' the UI thread.
    
                incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value += 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
                decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
                   Sub(progressBar) progressBar.Value -= 1,
                   New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                           .TaskScheduler = uiTaskScheduler})
    
            End Sub
            ' </snippet4>
    
            ' <snippet5>
            ' Event handler for the Add Work Items button.
            Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
                ' The Cancel button is disabled when the pipeline is not active.
                ' Therefore, create the pipeline and enable the Cancel button
                ' if the Cancel button is disabled.
                If Not toolStripButton2.Enabled Then
                    CreatePipeline()
    
                    ' Enable the Cancel button.
                    toolStripButton2.Enabled = True
                End If
    
                ' Post several work items to the head of the pipeline.
                For i As Integer = 0 To 4
                    toolStripProgressBar1.Value += 1
                    startWork.Post(New WorkItem())
                Next i
            End Sub
            ' </snippet5>
    
            ' <snippet6>
            ' Event handler for the Cancel button.
            Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
                ' Disable both buttons.
                toolStripButton1.Enabled = False
                toolStripButton2.Enabled = False
    
                ' Trigger cancellation.
                cancellationSource.Cancel()
    
                Try
                    ' Asynchronously wait for the pipeline to complete processing and for
                    ' the progress bars to update.
                    Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
                Catch e1 As OperationCanceledException
                End Try
    
                ' Increment the progress bar that tracks the number of cancelled
                ' work items by the number of active work items.
                toolStripProgressBar4.Value += toolStripProgressBar1.Value
                toolStripProgressBar4.Value += toolStripProgressBar2.Value
    
                ' Reset the progress bars that track the number of active work items.
                toolStripProgressBar1.Value = 0
                toolStripProgressBar2.Value = 0
    
                ' Enable the Add Work Items button.
                toolStripButton1.Enabled = True
            End Sub
            ' </snippet6>
    
            Protected Overrides Sub Finalize()
                cancellationSource.Dispose()
                MyBase.Finalize()
            End Sub
        End Class
    End Namespace
    ' </snippet100>

Example

The following example shows the complete code for Form1.cs (Form1.vb for Visual Basic).

sample 路 csharpsamples/snippets/csharp/VS_Snippets_Misc/tpldataflow_cancellationwinforms/cs/cancellationwinforms/form1.cs100

csharp
// <snippet100>
// <snippet1>
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
// </snippet1>

namespace CancellationWinForms
{
   public partial class Form1 : Form
   {
      // <snippet2>
      // A placeholder type that performs work.
      class WorkItem
      {
         // Performs work for the provided number of milliseconds.
         public void DoWork(int milliseconds)
         {
            // For demonstration, suspend the current thread.
            Thread.Sleep(milliseconds);
         }
      }
      // </snippet2>

      // <snippet3>
      // Enables the user interface to signal cancellation.
      CancellationTokenSource cancellationSource;

      // The first node in the dataflow pipeline.
      TransformBlock<WorkItem, WorkItem> startWork;

      // The second, and final, node in the dataflow pipeline.
      ActionBlock<WorkItem> completeWork;

      // Increments the value of the provided progress bar.
      ActionBlock<ToolStripProgressBar> incrementProgress;

      // Decrements the value of the provided progress bar.
      ActionBlock<ToolStripProgressBar> decrementProgress;

      // Enables progress bar actions to run on the UI thread.
      TaskScheduler uiTaskScheduler;
      // </snippet3>

      public Form1()
      {
         InitializeComponent();

         // Create the UI task scheduler from the current synchronization
         // context.
         uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
      }

      // <snippet4>
      // Creates the blocks that participate in the dataflow pipeline.
      private void CreatePipeline()
      {
         // Create the cancellation source.
         cancellationSource = new CancellationTokenSource();

         // Create the first node in the pipeline.
         startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
         {
            // Perform some work.
            workItem.DoWork(250);

            // Decrement the progress bar that tracks the count of
            // active work items in this stage of the pipeline.
            decrementProgress.Post(toolStripProgressBar1);

            // Increment the progress bar that tracks the count of
            // active work items in the next stage of the pipeline.
            incrementProgress.Post(toolStripProgressBar2);

            // Send the work item to the next stage of the pipeline.
            return workItem;
         },
         new ExecutionDataflowBlockOptions
         {
            CancellationToken = cancellationSource.Token
         });

         // Create the second, and final, node in the pipeline.
         completeWork = new ActionBlock<WorkItem>(workItem =>
         {
            // Perform some work.
            workItem.DoWork(1000);

            // Decrement the progress bar that tracks the count of
            // active work items in this stage of the pipeline.
            decrementProgress.Post(toolStripProgressBar2);

            // Increment the progress bar that tracks the overall
            // count of completed work items.
            incrementProgress.Post(toolStripProgressBar3);
         },
         new ExecutionDataflowBlockOptions
         {
            CancellationToken = cancellationSource.Token,
            MaxDegreeOfParallelism = 2
         });

         // Connect the two nodes of the pipeline. When the first node completes,
         // set the second node also to the completed state.
         startWork.LinkTo(
            completeWork, new DataflowLinkOptions { PropagateCompletion = true });

         // Create the dataflow action blocks that increment and decrement
         // progress bars.
         // These blocks use the task scheduler that is associated with
         // the UI thread.

         incrementProgress = new ActionBlock<ToolStripProgressBar>(
            progressBar => progressBar.Value++,
            new ExecutionDataflowBlockOptions
            {
               CancellationToken = cancellationSource.Token,
               TaskScheduler = uiTaskScheduler
            });

         decrementProgress = new ActionBlock<ToolStripProgressBar>(
            progressBar => progressBar.Value--,
            new ExecutionDataflowBlockOptions
            {
               CancellationToken = cancellationSource.Token,
               TaskScheduler = uiTaskScheduler
            });
      }
      // </snippet4>

      // <snippet5>
      // Event handler for the Add Work Items button.
      private void toolStripButton1_Click(object sender, EventArgs e)
      {
         // The Cancel button is disabled when the pipeline is not active.
         // Therefore, create the pipeline and enable the Cancel button
         // if the Cancel button is disabled.
         if (!toolStripButton2.Enabled)
         {
            CreatePipeline();

            // Enable the Cancel button.
            toolStripButton2.Enabled = true;
         }

         // Post several work items to the head of the pipeline.
         for (int i = 0; i < 5; i++)
         {
            toolStripProgressBar1.Value++;
            startWork.Post(new WorkItem());
         }
      }
      // </snippet5>

      // <snippet6>
      // Event handler for the Cancel button.
      private async void toolStripButton2_Click(object sender, EventArgs e)
      {
         // Disable both buttons.
         toolStripButton1.Enabled = false;
         toolStripButton2.Enabled = false;

         // Trigger cancellation.
         cancellationSource.Cancel();

         try
         {
            // Asynchronously wait for the pipeline to complete processing and for
            // the progress bars to update.
            await Task.WhenAll(
               completeWork.Completion,
               incrementProgress.Completion,
               decrementProgress.Completion);
         }
         catch (OperationCanceledException)
         {
         }

         // Increment the progress bar that tracks the number of cancelled
         // work items by the number of active work items.
         toolStripProgressBar4.Value += toolStripProgressBar1.Value;
         toolStripProgressBar4.Value += toolStripProgressBar2.Value;

         // Reset the progress bars that track the number of active work items.
         toolStripProgressBar1.Value = 0;
         toolStripProgressBar2.Value = 0;

         // Enable the Add Work Items button.
         toolStripButton1.Enabled = true;
      }
      // </snippet6>

      ~Form1()
      {
         cancellationSource.Dispose();
      }
   }
}
// </snippet100>

sample 路 vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms/form1.vb100

vb
' <snippet100>
' <snippet1>
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow

' </snippet1>

Namespace CancellationWinForms
    Partial Public Class Form1
        Inherits Form
        ' <snippet2>
        ' A placeholder type that performs work.
        Private Class WorkItem
            ' Performs work for the provided number of milliseconds.
            Public Sub DoWork(ByVal milliseconds As Integer)
                ' For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds)
            End Sub
        End Class
        ' </snippet2>

        ' <snippet3>
        ' Enables the user interface to signal cancellation.
        Private cancellationSource As CancellationTokenSource

        ' The first node in the dataflow pipeline.
        Private startWork As TransformBlock(Of WorkItem, WorkItem)

        ' The second, and final, node in the dataflow pipeline.
        Private completeWork As ActionBlock(Of WorkItem)

        ' Increments the value of the provided progress bar.
        Private incrementProgress As ActionBlock(Of ToolStripProgressBar)

        ' Decrements the value of the provided progress bar.
        Private decrementProgress As ActionBlock(Of ToolStripProgressBar)

        ' Enables progress bar actions to run on the UI thread.
        Private uiTaskScheduler As TaskScheduler
        ' </snippet3>

        Public Sub New()
            InitializeComponent()

            ' Create the UI task scheduler from the current synchronization
            ' context.
            uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
        End Sub

        ' <snippet4>
        ' Creates the blocks that participate in the dataflow pipeline.
        Private Sub CreatePipeline()
            ' Create the cancellation source.
            cancellationSource = New CancellationTokenSource()

            ' Create the first node in the pipeline.
            startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                      ' Perform some work.
                                                                      ' Decrement the progress bar that tracks the count of
                                                                      ' active work items in this stage of the pipeline.
                                                                      ' Increment the progress bar that tracks the count of
                                                                      ' active work items in the next stage of the pipeline.
                                                                      ' Send the work item to the next stage of the pipeline.
                                                                      workItem.DoWork(250)
                                                                      decrementProgress.Post(toolStripProgressBar1)
                                                                      incrementProgress.Post(toolStripProgressBar2)
                                                                      Return workItem
                                                                  End Function,
            New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})

            ' Create the second, and final, node in the pipeline.
            completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                            ' Perform some work.
                                                            ' Decrement the progress bar that tracks the count of
                                                            ' active work items in this stage of the pipeline.
                                                            ' Increment the progress bar that tracks the overall
                                                            ' count of completed work items.
                                                            workItem.DoWork(1000)
                                                            decrementProgress.Post(toolStripProgressBar2)
                                                            incrementProgress.Post(toolStripProgressBar3)
                                                        End Sub,
            New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                    .MaxDegreeOfParallelism = 2})

            ' Connect the two nodes of the pipeline. When the first node completes,
            ' set the second node also to the completed state.
            startWork.LinkTo(
               completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})

            ' Create the dataflow action blocks that increment and decrement
            ' progress bars.
            ' These blocks use the task scheduler that is associated with
            ' the UI thread.

            incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
               Sub(progressBar) progressBar.Value += 1,
               New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                       .TaskScheduler = uiTaskScheduler})

            decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
               Sub(progressBar) progressBar.Value -= 1,
               New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                       .TaskScheduler = uiTaskScheduler})

        End Sub
        ' </snippet4>

        ' <snippet5>
        ' Event handler for the Add Work Items button.
        Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
            ' The Cancel button is disabled when the pipeline is not active.
            ' Therefore, create the pipeline and enable the Cancel button
            ' if the Cancel button is disabled.
            If Not toolStripButton2.Enabled Then
                CreatePipeline()

                ' Enable the Cancel button.
                toolStripButton2.Enabled = True
            End If

            ' Post several work items to the head of the pipeline.
            For i As Integer = 0 To 4
                toolStripProgressBar1.Value += 1
                startWork.Post(New WorkItem())
            Next i
        End Sub
        ' </snippet5>

        ' <snippet6>
        ' Event handler for the Cancel button.
        Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
            ' Disable both buttons.
            toolStripButton1.Enabled = False
            toolStripButton2.Enabled = False

            ' Trigger cancellation.
            cancellationSource.Cancel()

            Try
                ' Asynchronously wait for the pipeline to complete processing and for
                ' the progress bars to update.
                Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
            Catch e1 As OperationCanceledException
            End Try

            ' Increment the progress bar that tracks the number of cancelled
            ' work items by the number of active work items.
            toolStripProgressBar4.Value += toolStripProgressBar1.Value
            toolStripProgressBar4.Value += toolStripProgressBar2.Value

            ' Reset the progress bars that track the number of active work items.
            toolStripProgressBar1.Value = 0
            toolStripProgressBar2.Value = 0

            ' Enable the Add Work Items button.
            toolStripButton1.Enabled = True
        End Sub
        ' </snippet6>

        Protected Overrides Sub Finalize()
            cancellationSource.Dispose()
            MyBase.Finalize()
        End Sub
    End Class
End Namespace
' </snippet100>

The following illustration shows the running application.

The Windows Forms Application

See also