Laden...

Backgroundworker: nullreferenceexception bei ProgressChanged

Erstellt von Rubeen vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.741 Views
Rubeen Themenstarter:in
7 Beiträge seit 2015
vor 8 Jahren
Backgroundworker: nullreferenceexception bei ProgressChanged

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.

  • Rubeen
S
8 Beiträge seit 2015
vor 8 Jahren

ich denke, dass hier etwas fehlt:

backgroundWorker.RunWorkerAsync();

Hier musst du ein Delegate übergeben, die beim Starten ausgeführt wird.

Rubeen Themenstarter:in
7 Beiträge seit 2015
vor 8 Jahren

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.

127 Beiträge seit 2015
vor 8 Jahren

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.

D
985 Beiträge seit 2014
vor 8 Jahren

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().

Rubeen Themenstarter:in
7 Beiträge seit 2015
vor 8 Jahren

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.

Information von Abt vor 8 Jahren

Bitte endlich [Hinweis] Wie poste ich richtig? Punkt 2.3 beachten!

127 Beiträge seit 2015
vor 8 Jahren

Nein, Ja ...

Die Events ProgressChanged und RunWorkerCompleted 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.

D
985 Beiträge seit 2014
vor 8 Jahren

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.