This article has been localized into German by the community.
Abbrechen des BackgroundWorker
Wie wir im vorherigen Artikel gesehen haben, hat das Multithreading den zusätzlichen Vorteil, dass es den Fortschritt anzeigen kann und Ihre Anwendung nicht hängen bleibt, während Sie zeitaufwändige Operationen durchführen.
Ein weiteres Problem, mit dem Sie konfrontiert werden, wenn Sie die gesamte Arbeit im UI-Thread durchführen, ist die Tatsache, dass es für den Benutzer keine Möglichkeit gibt, eine laufende Aufgabe abzubrechen - und warum ist das so? Nun, wenn der UI-Thread damit beschäftigt ist, Ihre langwierige Aufgabe auszuführen, wird keine Eingabe verarbeitet, was bedeutet, dass, egal wie stark Ihr Benutzer auf eine Cancel-Taste oder die Esc-Taste drückt, nichts passiert.
Zum Glück für uns ist der BackgroundWorker so gebaut, dass mit wenig Aufwand Fortschritt und Abbrechen unterstützt werden können, und während wir uns den Fortschritts-Teil im vorherigen Kapitel angesehen haben, geht es in diesem Kapitel nur darum, wie Sie einen Hintergrundvorgang abbrechen können. Lassen Sie uns direkt zu einem Beispiel springen:
<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;
}
}
}
}
Das XAML ist also sehr reduziert - nur ein Label zur Anzeige des aktuellen Status und dann ein paar Buttons zum Starten und Abbrechen des Workers.
In Code-behind beginnen wir mit der Erstellung der BackgroundWorker-Instanz. Achten Sie besonders auf die Eigenschaften WorkerSupportsCancellation und WorkerReportsProgress, die wir auf true setzen - ohne diese würde eine Ausnahme ausgelöst werden, wenn wir versuchten, diese Funktionen zu verwenden.
Die Schaltfläche Abbrechen ruft einfach die Methode CancelAsync() auf - dies signalisiert dem Worker, dass der Benutzer den laufenden Prozess abbrechen möchte, indem er die Eigenschaft CancellationPending auf true setzt, aber das ist alles, was Sie im UI-Thread tun können - der Rest muss innerhalb des DoWork-Ereignisses erfolgen.
Im DoWork-Ereignis zählen wir bis 100 mit einer Verzögerung von 250 Millisekunden bei jeder Iteration. Dies gibt uns eine schöne und lang laufende Aufgabe, für die wir den Fortschritt melden können und noch Zeit haben, auf die Schaltfläche Abbrechen zu klicken.
Beachten Sie, wie ich die CancellationPending-Eigenschaft bei jeder Iteration überprüfe - wenn der Worker abgebrochen wird, wird diese Eigenschaft wahr sein und ich habe die Möglichkeit, aus der Schleife zu springen. Ich setze die Cancel-Eigenschaft auf die Ereignisargumente, um zu signalisieren, dass der Prozess abgebrochen wurde - dieser Wert wird im Ereignis RunWorkerCompleted verwendet, um zu sehen, ob der Worker tatsächlich abgeschlossen wurde oder ob er abgebrochen wurde.
Im Ereignis RunWorkerCompleted überprüfe ich einfach, ob der Worker abgebrochen wurde oder nicht und aktualisiere dann das Status-Label entsprechend.
Zussammenfassung
Zusammenfassend lässt sich sagen, dass das Hinzufügen von Abbrechen-Unterstützung zu Ihrem BackgroundWorker einfach ist - stellen Sie einfach sicher, dass Sie die WorkerSupportsCancellation-Eigenschaft auf true setzen und prüfen Sie die CancellationPending-Eigenschaft während der Ausführung der Arbeit. Wenn Sie dann die Aufgabe abbrechen möchten, müssen Sie nur die Methode CancelAsync() auf dem Worker aufrufen und schon sind Sie fertig.