Windows Forms: Hintergrundarbeiten

Das Problem bei einer Anwendung mit grafischer Oberfläche ist, dass das Formular und die dahinter ausgeführten Funktionen innerhalb eines Threads arbeiten. Bei langwierigen Aufgaben haben wir also das Problem, dass die GUI nicht mehr bedient werden kann, solange eine Funktion abgearbeitet wird. Dies ist meistens jedoch nicht erwünscht und zudem schlechter Programmierstil. Deshalb sollte ein eigener Thread erstellt werden. Über das (unsichtbare) Steuerelement BackgroundWorker (Namensraum System.ComponentModel) können wir eine solche Funktionalität nutzen, welche in einer eigenen Klasse gekapselt ist und einige Vorteile bietet.
Mit der Eigenschaft IsBusy kann abgefragt werden, ob der BackgroundWorker aktuell arbeitet. Das Event DoWork wird ausgelöst, wenn die Funktion RunWorkerAsync() aufgerufen und somit der BackgroundWorker gestartet wird. Die im Event RunWorkerCompleted hinterlegte Ereignis-Funktion wird aufgerufen, wenn der BackgroundWorker abgebrochen wurde oder dieser abgeschlossen wurde. Um den BackgroundWorker abzubrechen, müssen wir die Funktion CancelAsync() aufrufen. Wichtig ist, dass hierdurch der BackgroundWorker nicht wirklich abgebrochen wird. Hierfür muss in der Event-Funktion des DoWork-Events die Eigenschaft CancellationPending abgerufen werden (siehe Beispiel). Das Abbrechen eines BackgroundWorker-Steuerelementes ist nur dann erlaubt, wenn die Eigenschaft WorkerSupportsCancellation auf true gesetzt ist. Ein Vorteil beim BackgroundWorker ist, dass wir über das Event ProgessChanged und die Funktion ReportProgess() einen Fortschritt melden und somit im Formular anzeigen können (z. B. Fortschritts-Balken aktualisieren). Diese Funktionalität kann nur genutzt werden, wenn WorkerReportsProgess auf true eingestellt ist. Dieser Text enthält viele Namen von Eigenschaften, Funktionen und Events, weshalb es umso wichtiger ist, sich das dazugehörige Beispiel anzuschauen, um den Sinn und Zweck zu verstehen.

Form1.cs

private void buttonStarten_Click(object sender, EventArgs e)
{
	// Abbruch-Button freigeben und Fortschrittsbalken zurücksetzen
	buttonAbbrechen.Enabled = true;
	buttonStarten.Enabled = false;
	progressBarFortschritt.Value = 0;

	// BackgroundWorker starten
	backgroundWorkerFortschritt.RunWorkerAsync();
}

private void buttonAbbrechen_Click(object sender, EventArgs e)
{
	// Start-Button freigeben
	buttonAbbrechen.Enabled = false;
	buttonStarten.Enabled = true;

	// BackgroundWorker abbrechen
	backgroundWorkerFortschritt.CancelAsync();
}

private void backgroundWorkerFortschritt_DoWork(object sender, DoWorkEventArgs e)
{
	for (int i = 1; i <= 100; i++)
	{
		// Prüfen ob Vorgang abgebrochen wurde (durch CancelAsync())
		if (backgroundWorkerFortschritt.CancellationPending)
		{
			e.Cancel = true;
			return;
		}

		// aktuelle Position in Fortschrittsbalken anzeigen und aktuelle Zahl speichern
		backgroundWorkerFortschritt.ReportProgress(i);
		e.Result = i;

		// 50 ms warten
		Thread.Sleep(50);
	}
}

private void backgroundWorkerFortschritt_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
	// Wert für Fortschrittsbalken setzen
	progressBarFortschritt.Value = e.ProgressPercentage;
}

private void backgroundWorkerFortschritt_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
	// Meldung ausgeben
	if (e.Cancelled)
		MessageBox.Show("Der Vorgang wurde abgebrochen!");
	else
		MessageBox.Show("Der erreichte Wert war " + (int)e.Result + "!");   // gibt immer 100 aus

	// Start-Button freigeben
	buttonAbbrechen.Enabled = false;
	buttonStarten.Enabled = true;
}
Download

LinksRechts