Hallo!
Nach langem Suchen hier und auch in anderen Foren, bin ich leider nicht auf eine Antwort auf mein Problem gestoßen.
Mein Problem ist folgendes. In einer Klasse "Sim", die die Hauptlogik des Programms darstellt, wird ein Feld "ladefenster" als "new FensterLaden()" deklariert. Dieses wird ebenfalls geöffnet. Nun wird ein Backgroundworker gestartet, welcher beim "ProgressChanged"-Event die Methode "_backgroundWorker_ProgressChanged" aufruft. In dieser soll eine Interaktion mit dem "ladefenster" stattfinden. Zur Verdeutlichung einige Ausschnitte aus dem Code:
#region usings
namespace ILS.Simulation
{
public class Sim
{
#region Deklarationen
/// <summary>
/// Fenster deklarieren
/// </summary>
public Hauptfenster HauptFenster { get; private set; }
/// <summary>
/// Andere Variablen
/// </summary>
private static Properties.Settings settings = Properties.Settings.Default; //Settings-Abkürzung für übersichtlicheren Code...
private ProgressBar ladeBalken; //Ladebalkenvariable erstellen
private FensterLaden ladeFenster; //Ladefenstervariable erstellen
private BackgroundWorker _backgroundWorker; //Backgroundworker für langwierige Aufgaben
#endregion
#region Vorbereitung
public Sim(Hauptfenster parent)
{
ladeFenster = new FensterLaden(this);
HauptFenster = new Hauptfenster(); ///Hauptfenster für Interaktionslogik
ladeFenster.Show(); //Die Methode Start() wird bei Window_Loaded() aufgerufen
ladeBalken = ladeFenster.LadeBalken;
}
public void Start()
{
_backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true };
_backgroundWorker.DoWork += Starten;
_backgroundWorker.ProgressChanged += _backgroundWorker_ProgressChanged;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
_backgroundWorker.RunWorkerAsync();
}
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("fertig");
HauptFenster.Show(); //Hauptfenster öffnen
}
private int fortschritt { get { return (100 / fortschrittsmax) * fortschrittaktuell; } }
private int _fortschrittsmax = 0;
private int fortschrittsmax { get { return _fortschrittsmax; } set { _fortschrittsmax = value; _backgroundWorker.ReportProgress(fortschritt); } }
private int _fortschrittaktuell = 0;
private int fortschrittaktuell { get { return _fortschrittaktuell; } set { _fortschrittaktuell = value; _backgroundWorker.ReportProgress(fortschritt); } }
private void Starten(object sender, DoWorkEventArgs e)
{
fortschrittsmax = 10;
//Einige Arbeit
fortschrittaktuell++; //Kleiner Ladeschritt weiter
_backgroundWorker.ReportProgress(fortschritt, "Fenster"); //"Fenster" übergeben, sodass UI Fenster aufbaut
//Mehr Arbeit
fortschrittaktuell = 100;
ladeFenster.Close();
}
#endregion Vorbereitung
#region events
private void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Ein Haufen von nullreferenceexceptions:
MessageBox.Show(e.ProgressPercentage.ToString()); //=0...
ladeBalken.Value = e.ProgressPercentage;
ladeFenster.TaskbarItemInfo.ProgressValue = e.ProgressPercentage;
ladeFenster.LadeText.Text = "Geladen zu " + e.ProgressPercentage + " %";
if (e.UserState.ToString() == "Fenster")
fensterErstellen(); ///Fenster erstellen für schnelleren Aufruf / Vorausladen der Controls
}
#endregion
}
}
Ich hoffe, dass der Code ausreicht. Was könnte hier das Problem sein, was habe ich falsch gemacht? Ich beschäftige mich das erste mal mit diesem Thema, also habt Verständnis... Der Backgroundworker liest vorallem Daten aus einer Datenbank und bereitet einige Fenster vor.
ich denke, dass hier etwas fehlt:
backgroundWorker.RunWorkerAsync();
Hier musst du ein Delegate übergeben, die beim Starten ausgeführt wird.
ich denke, dass hier etwas fehlt:
backgroundWorker.RunWorkerAsync();
Hier musst du ein Delegate übergeben, die beim Starten ausgeführt wird.
Danke, das hat super funktioniert! Jetzt muss ich noch ein paar andere Dinge im Code ändern, damit alles so funktioniert, wie ich es haben möchte..... So sieht die Zeile auf jeden Fall nun aus:
_backgroundWorker.DoWork += delegate (object s, DoWorkEventArgs args) { Starten(); };
Bloß beim Ereignis, wenn es fertig ist, hat es nicht ganz so funktioniert.
Vermutlich funktioniert das "RunWorkerCompleted" Ereignis nicht weil du versuchst daraus eine MessageBox anzuzeigen.
Aus Hintergrundthreads darf man nicht direkt mit UI Elementen interagieren, das gibt in der Regel eine CrossThreadException.
In meiner Erfahrung ist das mit den CrossThreadExceptions so eine Sache. Manchmal werden diese zuverlässig angezeigt, oft ist es aber auch so dass das Programm eine sehr merkwürdige /undefinierte Verhaltensweise an den Tag legt.
Wenn du nach "CrossThread Exception" suchst, findest du vermutlich genug um das Problem zu korrigieren.
Vermutlich funktioniert das "RunWorkerCompleted" Ereignis nicht weil du versuchst daraus eine MessageBox anzuzeigen.
Aus Hintergrundthreads darf man nicht direkt mit UI Elementen interagieren, das gibt in der Regel eine CrossThreadException.
Nein, Ja ...
Die Events ProgressChanged
und RunWorkerCompleted
laufen beide im UIThread und eben speziell dafür gedacht dort die Controls zu aktualisieren.
Allerdings ist ladeFenster.Close()
im DoWork
EventHandler absolut falsch. Das gehört in RunWorkerCompleted
.
Eine MessageBox
im ProgressChanged
anzuzeigen ist natürlich auch nicht der Brüller.
Dann wäre da auch noch e.UserState.ToString()
.
Die MessageBoxen waren zum Testen, ob es funktioniert. Habe den Code natürlich wieder entfernt. Doch im "RunWorkerCompleted" bekomme ich eine Exception, wenn ich versuche, "ladeFenster.Close()" auszuführen. Deshalb steht diese Zeile Code an anderer Stelle.
Bitte endlich [Hinweis] Wie poste ich richtig? Punkt 2.3 beachten!
Nein, Ja ...
Die Events
ProgressChanged
undRunWorkerCompleted
laufen beide im UIThread und eben speziell dafür gedacht dort die Controls zu aktualisieren.
Naja ... auch die Antwort ist, wenn man dem folgenden glauben schenken mag, nur bedingt korrekt:
BackgroundWorker RunWorkerCompleted Event
Es ist also davon abhängig auf welchem Thread der Backgroundthread gestartet wird.
Ja, sicher, allerdings ist die Grundintention des BackgroundWorker
, dass dieser vom UIThread aus gestartet wird.
MS sagt nämlich dazu:
Im DoWork-Ereignishandler dürfen keine Benutzeroberflächenobjekte bearbeitet werden. Verwenden Sie stattdessen zum Kommunizieren mit der Benutzeroberfläche das ProgressChanged-Ereignis und das RunWorkerCompleted-Ereignis.