TOC

This article has been localized into Spanish by the community.

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.

En Código subyacente, comenzamos creando la instancia de BackgroundWorker. Preste especial atención a las propiedades WorkerSupportsCancellation y WorkerReportsProgress que establecemos como verdaderas; sin eso, se generará una excepción si intentamos utilizar estas funciones.

El botón cancelar simplemente llama al método CancelAsync () : esto le indicará al trabajador que el usuario desea cancelar la ejecución del proceso estableciendo la propiedad CancellationPending en true, pero eso es todo lo que puede hacer desde el hilo de la interfaz de usuario: el resto tendrá que hacerse desde dentro del evento DoWork .

En el evento DoWork , contamos hasta 100 con un retraso de 250 milisegundos en cada iteración. Esto nos da una tarea agradable y larga, para lo cual puede informar el progreso y aún tiene tiempo de presionar el botón Cancelar.

Observe cómo verifico la propiedad CancellationPending en cada iteración: si el trabajador se cancela, esta propiedad será verdadera y tendré la oportunidad de saltar fuera del circuito. Establecí la propiedad Cancelar en los argumentos del evento, para indicar que el proceso se canceló, este valor se usa en el evento RunWorkerCompleted para ver si el trabajador realmente se completó o si se canceló.

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

En resumen, agregar soporte de cancelación a su BackgroundWorker es fácil, solo asegúrese de configurar la propiedad WorkerSupportsCancellation a true y verifique la propiedad CancellationPending mientras realiza el trabajo. Luego, cuando desea cancelar la tarea, simplemente tiene que llamar al método CancelAsync () en el trabajador y ya está.


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!