This article has been localized into Portuguese by the community.
Cancelando o BackgroundWorker
Como vimos no artigo anterior, o multi-threading tem a vantagem adicional de poder mostrar progresso e não ter seu aplicativo travado durante a operação demorada.
Outro problema que você enfrentará se realizar todo o trabalho no thread da interface do usuário é o fato de que não há como o usuário cancelar uma tarefa em execução - e por que isso acontece? Porque, se o thread da interface do usuário estiver ocupado realizando sua tarefa demorada, nenhuma entrada será processada, o que significa que não importa o quanto o usuário pressione o botão Cancelar ou a tecla Esc, nada acontece.
Felizmente para nós, o BackgroundWorker foi criado para facilitar o suporte ao progresso e cancelamento, e enquanto analisamos toda a parte do progresso no capítulo anterior, este será sobre como usar o suporte de cancelamento. Vamos pular direto para um exemplo:
<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;
}
}
}
}
Portanto, o XAML é muito fundamental - apenas um rótulo para mostrar o status atual e, em seguida, alguns botões para iniciar e cancelar o trabalhador.
Em Code-behind, começamos criando a instância BackgroundWorker. Preste especial atenção às propriedades WorkerSupportsCancellation e WorkerReportsProgress que definimos como verdadeiras - sem isso, uma exceção será lançada se tentarmos usar esses recursos.
O botão cancelar simplesmente chama o método CancelAsync () - isso sinalizará ao funcionário que o usuário gostaria de cancelar o processo em execução definindo a propriedade CancellationPending como true, mas isso é tudo o que você pode fazer no thread da interface do usuário - o restante terá que ser feito dentro do evento DoWork .
No evento DoWork , contamos até 100 com um atraso de 250 milissegundos em cada iteração. Isso nos dá uma boa e longa tarefa, para a qual podemos relatar o progresso e ainda ter tempo de acertar o botão Cancelar.
Observe como eu verifico a propriedade CancellationPending em cada iteração - se o trabalhador for cancelado, essa propriedade será verdadeira e terei a oportunidade de sair do loop. Eu defino a propriedade Cancelar nos argumentos do evento, para sinalizar que o processo foi cancelado - esse valor é usado no evento RunWorkerCompleted para ver se o trabalhador realmente foi concluído ou se foi cancelado.
No evento RunWorkerCompleted , basta verificar se o trabalhador foi cancelado ou não e atualizar o rótulo de status de acordo.
Resumo
Então, para resumir, adicionar suporte de cancelamento ao seu BackgroundWorker é fácil - apenas certifique-se de definir a propriedade WorkerSupportsCancellation como true e verifique a propriedade CancellationPending ao executar o trabalho. Então, quando você quiser cancelar a tarefa, basta chamar o método CancelAsync () no worker e pronto.