TOC

This article is currently in the process of being translated into Spanish (~47% done).

Misceláneos:

Interrumpir el BackgroundWorker

Como hemos visto en el artículo anterior, la programación multihilo tiene la ventaja añadida de ser capaz de mostrar el progreso y no colgar la aplicación mientras se realiza una operación que toma mucho tiempo.

Otro problema que encontrarás si realizas todo el trabajo en el hilo de la interfaz de usuario es el hecho de que el usuario no tiene forma de cancelar una tarea que está ejecutándose - ¿y por que pasa esto? Porque el hilo de la interfaz de usuario está ocupado realizando una larga tarea, por lo que ninguna entrada de usuario será procesada, lo que significa que no importa cómo de fuerte el usuario golpee el botón de Cancelar o la tecla Escape, puesto que nada va a ocurrir.

Afortunadamente para nosotros, el BackgroundWorker está construido para soportar fácilmente el estado del progreso y la interrupción, y mientras que ya hemos visto la parte sobre el progreso en el capítulo anterior, en este trataremos el soporte para la interrupción. Saltemos directamente al ejemplo:

<Window x:Class="WpfTutorialSamples.Misc.BackgroundWorkerCancellationSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="BackgroundWorkerCancellationSample" Height="120" Width="200">
    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
        <TextBlock Name="lblStatus" HorizontalAlignment="Center" Margin="0,10" FontWeight="Bold">Not running...</TextBlock>
        <WrapPanel>
            <Button Name="btnStart" Width="60" Margin="10,0" Click="btnStart_Click">Start</Button>
            <Button Name="btnCancel" Width="60" Click="btnCancel_Click">Cancel</Button>
        </WrapPanel>
    </StackPanel>
</Window>
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

namespace WpfTutorialSamples.Misc
{
	public partial class BackgroundWorkerCancellationSample : Window
	{
		private BackgroundWorker worker = null;

		public BackgroundWorkerCancellationSample()
		{
			InitializeComponent();
			worker = new BackgroundWorker();
			worker.WorkerSupportsCancellation = true;
			worker.WorkerReportsProgress = true;
			worker.DoWork += worker_DoWork;
			worker.ProgressChanged += worker_ProgressChanged;
			worker.RunWorkerCompleted += worker_RunWorkerCompleted;
		}

		private void btnStart_Click(object sender, RoutedEventArgs e)
		{
			worker.RunWorkerAsync();
		}

		private void btnCancel_Click(object sender, RoutedEventArgs e)
		{
			worker.CancelAsync();
		}

		void worker_DoWork(object sender, DoWorkEventArgs e)
		{
			for(int i = 0; i <= 100; i++)
			{
				if(worker.CancellationPending == true)
				{
					e.Cancel = true;
					return;
				}
				worker.ReportProgress(i);
				System.Threading.Thread.Sleep(250);
			}
			e.Result = 42;
		}

		void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			lblStatus.Text = "Working... (" + e.ProgressPercentage + "%)";
		}

		void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if(e.Cancelled)
			{
				lblStatus.Foreground = Brushes.Red;
				lblStatus.Text = "Cancelled by user...";
			}
			else
			{
				lblStatus.Foreground = Brushes.Green;
				lblStatus.Text = "Done... Calc result: " + e.Result;
			}
		}
	}
}

Como ves, el código XAML es muy simple - tan sólo una etiqueta para mostrar el estado actual y después un par de botones para iniciar y parar el worker.

In Code-behind, we start off by creating the BackgroundWorker instance. Pay special attention to the WorkerSupportsCancellation and WorkerReportsProgress properties which we set to true - without that, an exception will be thrown if we try to use these features.

The cancel button simply calls the CancelAsync() method - this will signal to the worker that the user would like to cancel the running process by setting the CancellationPending property to true, but that is all you can do from the UI thread - the rest will have to be done from inside the DoWork event.

In the DoWork event, we count to 100 with a 250 millisecond delay on each iteration. This gives us a nice and lengthy task, for which we can report progress and still have time to hit that Cancel button.

Notice how I check the CancellationPending property on each iteration - if the worker is cancelled, this property will be true and I will have the opportunity to jump out of the loop. I set the Cancel property on the event arguments, to signal that the process was cancelled - this value is used in the RunWorkerCompleted event to see if the worker actually completed or if it was cancelled.

En el evento RunWorkerCompleted, simplemente compruebo si el worker ha sido cancelado o no y entonces actualizo la etiqueta que muestra el estado de manera acorde.

Resumen

So to sum up, adding cancellation support to your BackgroundWorker is easy - just make sure that you set WorkerSupportsCancellation property to true and check the CancellationPending property while performing the work. Then, when you want to cancel the task, you just have to call the CancelAsync() method on the worker and you're done.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!