C# Delegaten/Invoke/Thread/Backgroundworker?

2 Antworten

Meine Vermutung ist, dass label 1 ja bereits Form1 Thread läuft und daher nicht mehr geändert werden kann über einen Backgroundworker.

Genau. Wenn du Elemente, die von unterschiedlichen Threads geteilt werden, nutzen möchtest, muss dies synchronisiert erfolgen. Teile also strikt auf in View und Model. Das Model wird durch ein String-Property repräsentiert, welches geblockt werden muss, bevor eine Veränderung am Wert vorgenommen werden darf. An dieses Property wird das Label gebunden.

Lies dazu hier weiter:

System.ObjectDisposedException: "Auf das verworfene Objekt kann nicht zugegriffen werden.Objektname: "Skynet"."

Du gibst das Objekt ja auch nach Nutzung explizit zur Löschung frei.

hello.Dispose();
Nevron 
Fragesteller
 14.12.2017, 16:45

Hab das Dispose gelöscht. Der Fehler ist aber noch immer vorhanden :)

0
regex9  14.12.2017, 16:55
@Nevron

Recherchiere mal, was die Close-Methode macht.

this.hello.Close();
0
regex9  15.12.2017, 04:53
@Nevron
huhu, wie du gesagt hattest, hab ich versucht das label1 zu blocken. Leider mit mäßigen Erfolg. Eigentlich gar keiner. Er meckert nach wie vor an der Methode
private void Willkommensgruss() {
  lock (this.label1) {
    Thread.Sleep(2000);
    label1.Visible = true;
  }
}
Was mach ich nur falsch?

Nein, ich habe nicht geschrieben, dass du das Label blockieren sollst.

Ich werde mir das Ganze später noch einmal genauer anschauen und etwas dazu schreiben.

0

So, tut mir leid, dass ich nun so lange für eine Antwort benötigt habe, ich hatte die letzten Tage kaum Zeit und schreibe deswegen eine neue Antwort auf deine letzte Frage.

Zuerst einmal habe ich mir ein neues Windows Form erstellt und darauf ein Label (label1) sowie 2 Buttons (button1, button2) gelegt. Mein Code sieht folgendermaßen aus:

public partial class MyForm : Form, INotifyPropertyChanged
{
  public delegate void Worker(BackgroundWorker backgroundWorker);

  private BackgroundWorker _worker;

  private string _labelText;
      
  public MyForm()
  {
    _worker = new BackgroundWorker();
    
    InitializeComponent();
    PropertyChanged += HandleLabelTextChange;
                  
    button1.Click += delegate
    {
      Thread thread = new Thread(() => LabelText = "Hallo")
      {
        IsBackground = true
      };
      thread.Start();
    };

    _worker.DoWork += (sender, e) => 
    {
      LabelText = "Welcome";
      Thread.Sleep(2000);
    };
  }
                                    
  public string LabelText
  {
    get => _labelText;
    set 
    {
      _labelText = value;
      OnNotifyChanged("LabelText");
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
                                
  protected void OnNotifyChanged(string propertyName)
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }

  // bound to button2
  private void HandleButtonClick(object sender, EventArgs e)
  {
    var workerDelegate = new Worker(_worker.RunWorkerAsync);
    workerDelegate.Invoke(_worker);
  }

  private void HandleLabelTextChange(object sender, EventArgs e)
  {
    Invoke((MethodInvoker)delegate
    {
      label1.Text = LabelText;
    });
  }
}

Der Kürze halber habe ich Model und View in eine Klasse zusammengelegt. Die Aufgabe des Models besteht darin, den Text für das Label zu speichern und zu verwalten. Damit auf Änderungen an dem Text sofort reagiert werden kann, wird das INotifyPropertyChanged-Interface implementiert, welches ein Event definiert. Mit der Methode OnNotifyPropertyChanged wird das Event getriggert, als Information in das Eventobjekt der Name des Property gespeichert, welches sich geändert hat.

Für einen Klick auf den ersten Button lasse ich einen neuen Thread starten, welcher im Hintergrund läuft und das Property in seinem Wert verändert. Der zweite Button macht es so wie bei dir über eine Instanz der BackgroundWorker-Klasse.

Die eigentliche Magie geschieht nun in dem Handler für das PropertyChanged-Event. Er wird aufgerufen, nachdem die Änderung des Models getriggert wurde. Die Invoke-Methode führt einen threadsafe Request an die UI aus, dass das Label geändert werden soll.

Dieses Beispiel - und auch eines, welches nur den BackgroundWorker nutzt, wird in der Dokumentation mit einem guten Beispiel erklärt.

https://msdn.microsoft.com/de-de/library/ms171728(v=vs.110).aspx

Das Ändern des Labels muss auf dem Thread geschehen, der das Element erzeugt hat, der UI-Thread darf nicht blockiert werden.