Sunday, June 05, 2016

Simple WPF app using Task and Await with .NET 4.5

Here is a simple example showing how to create background tasks in a WPF app with .NET 4.5 and above. This is so much easier than the old BackgroundWorker approach.

<Window x:Class="TestAsyncTasks.MainWindow"
        Title="MainWindow" Height="350" Width="525">
 <Grid Margin="4">
   <RowDefinition Height="Auto"></RowDefinition>
   <RowDefinition Height="*"></RowDefinition>
  <StackPanel Grid.Row="0" Orientation="Horizontal">
   <Button x:Name="ButtonStart" Content="Start" Click="Start_Click" Padding="4"/>
   <Button x:Name="ButtonCancel" Content="Cancel" Click="Cancel_Click" Padding="4"/>
  <TextBox Grid.Row="1" x:Name="Label1"></TextBox>
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace TestAsyncTasks
 /// <summary>
 /// Interaction logic for MainWindow.xaml
 /// </summary>
 public partial class MainWindow : Window
  public MainWindow()
  // To allow background task to be cancelled with cts.Cancel()   
  CancellationTokenSource cts;
  private async void Start_Click(object sender, RoutedEventArgs e)
   // Disable button so it can't be clicked again until finished  
   ButtonStart.IsEnabled = false;
   Label1.Text = "Start\r\n";
   // Setup the function to be called with updates     
   var progressUpdate = new Progress<int>(ReportProgressOnUIThread);
   // Create a new CancellationTokenSource and optionally set a time 
   // when the task will automatically cancel if it has not already 
   // finished.              
   int AutoCancelAfterMS = 50000;
   cts = new CancellationTokenSource(AutoCancelAfterMS);
   // Must be in Try/Catch to trap the OperationCanceledException  
    int loopTo = 10;
    int result = await MyBackgroundTaskAsync(loopTo, progressUpdate, cts.Token);
    // This code does not run until MyBackgroundTaskAsync finishes 
    Label1.Text += $"Final result : {result}\r\n";
   catch (OperationCanceledException ex)
    // Do stuff to handle the cancellation exception    
    Label1.Text +=  "CANCELLED\r\n";
   catch (Exception ex)
    //Do stuff to handle other exceptions       
    Label1.Text += $"Exception : {ex.Message}\r\n";
   // Reenable Start button           
   ButtonStart.IsEnabled = true;
   Label1.Text += "Finished\r\n";
  private void Cancel_Click(object sender, RoutedEventArgs e)
  async Task<int> MyBackgroundTaskAsync(int DataFromParent, IProgress<int> progress, CancellationToken ct)
   int result = await Task.Run<int>(async () =>
    for (int n=0; n< DataFromParent; n++)
     //You cannot do this because it's running on a non UI thread
     // Label1.Text += $"{n} I don't work!\r\n";
     // Throw OperationCanceledException if cts.Cancel() called 
     // Report progress back to UI thread      
     // Do the slow things in the background     
     await SlowStuff();
    // result gets this value which is returned as final result 
    return 42;
    }, ct);
   return result;
  async Task SlowStuff()
   // Simulate some slow code. This runs on a background thread.  
   await Task.Delay(1000);
  void ReportProgressOnUIThread (int value)
   // This runs on the UI thread so it can update the WPF controls 
   Label1.Text += value.ToString() + "\r\n";

No comments: