This article has been localized into Czech by the community.
Zrušení BackgroundWorker
Jak jsme viděli v předchozím článku, vícevláknové zpracování má tu přidanou výhodu, že umožňuje zobrazovat průběh a nezpůsobuje zamrznutí aplikace při provádění časově náročných operací.
Dalším problémem, kterému čelíte, pokud veškerou práci provádíte na vlákně uživatelského rozhraní (UI), je skutečnost, že uživatel nemá možnost zrušit běžící úlohu - a proč tomu tak je? Protože pokud je vlákno UI zaneprázdněné prováděním vaší rozsáhlé úlohy, nebudou zpracovávány žádné vstupy, což znamená, že bez ohledu na to, jak moc uživatel mačká tlačítko Zrušit nebo klávesu Esc, nic se nestane.
Naštěstí pro nás je BackgroundWorker navržen tak, aby vám usnadnil podporu průběhu a zrušení, a zatímco jsme se v předchozí kapitole podívali na celou část týkající se průběhu, tato bude věnována tomu, jak používat podporu pro zrušení. Pojďme se rovnou podívat na příklad:
<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;
}
}
}
}
Takže XAML je velmi základní - jen štítek pro zobrazení aktuálního stavu a pak pár tlačítek pro spuštění a zrušení workeru.
V kódu za běhu začínáme vytvořením instance BackgroundWorker. Věnujte zvláštní pozornost vlastnostem WorkerSupportsCancellation a WorkerReportsProgress, které nastavíme na true - bez toho by byla vyhozena výjimka, pokud bychom se pokusili tyto funkce použít.
Tlačítko zrušení jednoduše zavolá metodu CancelAsync() - toto oznámí workeru, že uživatel by rád zrušil běžící proces nastavením vlastnosti CancellationPending na true, ale to je vše, co můžete udělat z vlákna UI - zbytek musí být proveden zevnitř události DoWork.
V události DoWork počítáme do 100 s 250 milisekundovým zpožděním při každé iteraci. To nám dává pěkný a dlouhý úkol, při kterém můžeme hlásit průběh a stále máme čas stisknout to tlačítko Zrušit.
Všimněte si, jak při každé iteraci kontroluji vlastnost CancellationPending - pokud je worker zrušen, tato vlastnost bude true a já budu mít možnost vyskočit z cyklu. Nastavím vlastnost Cancel na argumentech události, abych signalizoval, že proces byl zrušen - tato hodnota je použita v události RunWorkerCompleted, abychom viděli, zda worker skutečně dokončil úkol, nebo byl zrušen.
V události RunWorkerCompleted jednoduše zkontroluji, zda byl worker zrušen, a poté aktualizuji štítek stavu odpovídajícím způsobem.
Shrnutí
Souhrnem, přidání podpory pro zrušení vašeho BackgroundWorkeru je snadné - stačí se ujistit, že nastavíte vlastnost WorkerSupportsCancellation na true a během provádění práce kontrolujete vlastnost CancellationPending. Pak, když chcete úkol zrušit, stačí na workeru zavolat metodu CancelAsync() a máte hotovo.