2014-11-04 10 views
5

Ich habe zwei Klassenbeobachtbare Sammlung Änderung wird nicht von Hintergrund auf UI aktualisiert, aber eine normale Eigenschaft erhält Benachrichtigung

public class ConccClass<T> : ObservableCollection<T> 
{ 
} 

und

public class TestTherad: INotifyPropertyChanged 
{ 
    private string name; 
    public string Name 
    { get { 
     return name; 
    } 
     set 
     { 
      if (value != name) 
      { 
       name = value; 
       RaisePropertyChanged("Name"); 
      } 
     } 
    } 
    //// the notification implemented fully here 
} 

Jetzt habe ich eine Sammlung von ‚ConccClass‘ erstellt haben, in meiner Ansicht Modell und binde es mit Datagrid auf XAML in Sicht.

Frage Wenn ich ein Element auf einem Hintergrund-Thread einfach ohne irgendeinen Dispatcher hinzufügen, spiegelt es sich nicht in Datagrid wider. bedeutet, dass kein Gegenstand hinzugefügt wurde. Dazu muss ich den Artikel im Dispatcher hinzufügen. Beginne Invoke. Was für mich Sinn macht.

Aber um den Namen eines Artikels zu aktualisieren, brauche ich keinen Dispatcher.

Task.Factory.StartNew(() => 
     { 
      while (true) 
      { 
       Thread.Sleep(100); 
       { 
        this.Dispatcher.Invoke(() => this.Coll.Add(new TestTherad())); // **Works well** 
        //this.Coll.Add(new TestTherad()); // **does not work at all.** 
        this.Coll[0].Name = r.Next().ToString(); // ** without dispatcher works well.** 
       } 
      } 
     }); 

Warum solch ein Verhalten?

+0

Welche Version von .Net? – OmegaMan

+0

Mit dotPeek, in 'INotifyPropertyChanged' gibt es' Ereignis PropertyChangedEventHandler PropertyChanged; '. Ein Blick auf 'PropertyChangedEventHandler' zeigt, dass es sich um einen Delegaten mit dem folgenden Attribut' [HostProtection (SecurityAction.LinkDemand, SharedState = true)]] handelt. Ich frage mich, ob [SharedState] (http://msdn.microsoft.com/en-us/library/system.security.permissions.hostprotectionattribute.sharedstate (v = vs.110) .aspx) ermöglicht den Delegaten erfolgreich ausgeführt werden der Haupt-Thread, ohne dass er aufgerufen werden muss. –

+0

@OmegaMan - es ist VS 2012. –

Antwort

5

Es folgt eine kurze Erklärung:

Sie wahrscheinlich ein UI-Element auf die beobachtbaren Sammlung binded haben. Wenn Sie der beobachtbaren Sammlung ein Element hinzufügen, wird die Benutzeroberfläche aktualisiert, um Änderungen widerzuspiegeln. Der einzige Thread, der Änderungen an der Benutzeroberfläche vornehmen darf, ist jedoch der Hauptthread.

Wenn Sie also ein Objekt mit einem Hintergrundthread zur observablen Sammlung hinzufügen, versucht die Benutzeroberfläche, den Hintergrundthread zu verwenden, der keine Änderungen an der Benutzeroberfläche vornehmen darf, und es wird eine Ausnahme ausgelöst.

Ich bin mir ziemlich sicher, dass diese Zeile eine Ausnahme auslösen sollte: //this.Coll.Add(new TestTherad()) ;. Versuchen Sie das Debugging im Aufgabenblock.

Wenn Sie den Dispatcher verwenden, führen Sie das Update mit dem Hauptthread aus, und aus diesem Grund funktioniert es.

Das Update auf Eigenschaften funktioniert, weil Sie nur ein Ereignis auslösen. Das Framework sollte auf dieses Ereignis warten und sicherstellen, dass es automatisch zum Hauptthread gesendet wird.

+0

Ich habe getestet, dass selbst wenn ich die Name-Eigenschaft mit ContentControl gebunden habe, funktioniert es gut (keine Notwendigkeit von Textblock) ohne Dispatcher. Ich glaube immer noch, dass hinter diesem Verhalten ein versteckter Grund steckt. Natürlich weiß ich, dass eine Eigenschaft die UI nicht aktualisiert. Aber warum? –

+0

@DJ konnte Ihren Kommentar nicht verstehen. "Das Update auf Eigenschaften funktioniert, weil Sie nur ein Ereignis auslösen. Das Framework sollte auf dieses Ereignis warten und sicherstellen, dass es automatisch an den Hauptthread gesendet wird." ist nicht die Antwort? – Joe

+0

Entschuldigung für die späte Antwort. Aber in beiden Fällen wird das Ereignis in einem Sammeländerungsereignis ausgelöst, das auch auf die Benutzeroberfläche gehört. in einem anderen ist es auch ein Ereignis. Auch beide sind auf der Benutzeroberfläche gebunden. Wie ist es anders? –

3

Eine einfache Möglichkeit, diese Ausnahmen zu vermeiden, ist die Verwendung der BindableCollection von Caliburn.Micro. Dies ist eine ObservableCollection, die die CollectionChanged-Ereignisse automatisch an den Haupt-Thread sendet.

Verwenden Sie BindableCollection nur in Ihren ViewModels, da sich die CollectionChanged-Ereignisse im Hauptthread befinden und Sie aus Leistungsgründen den Großteil der Codierung für einen Hintergrundthread ausführen möchten.

+0

BindableCollection ist gute Sachen. Es ist eine gute Option, es sei denn, Sie planen, eine observablecollection mit Blockierung wie folgt zu schreiben: http://xcalibursystems.com/2014/02/making-a-better-observablecollection-part-2-cross-threading/ – Xcalibur37

+0

Danke für den Vorschlag, aber ich bin auf der Suche nach meiner Antwort. –

+1

'BindableCollection' ist defekt, und es verewigt die Lüge, dass es irgendwie sicher ist, eine Liste in einem Thread zu modifizieren, während sie in einem anderen Thread UI-gebunden ist. Neben expliziten Änderungsbenachrichtigungen gibt es Bedingungen, die dazu führen können, dass eine Sammlung möglicherweise während einer Aktualisierung abgefragt wird, was zu undefiniertem Verhalten führt. –

1

Die meisten der vorherigen Kommentare haben dies bereits beantwortet, dies sollte jedoch zusammenfassen. Vor .NET 4.5 konnten Sie nur Updates für eine ObservablCollection im UI-Thread aufrufen. Dies wird über den Aufruf von Dispatcher.Invoke erreicht. Der Aufruf zum Aktualisieren des Objektnamens hat keine Auswirkungen auf die Sammlungsereignisse usw., nur auf das Objekt. Das Aktualisieren der Objekteigenschaften sollte idealerweise auch nur auf dem UInthread durchgeführt werden. Je nachdem, wie die Eigenschaft gebunden ist, können Sie diese Eigenschaften jedoch manchmal von einem Nicht-UI-Thread aktualisieren. Wie bereits erwähnt, hängt dies davon ab, welche Version von .Net verwendet wird.

0

Bei Github finden Sie eine kleine Hilfsklasse namens AsyncObservableCollection: Github Es ist ein sehr dünner Wrapper um eine ObersarableCollection. Es übernimmt alle Threading-Probleme, die Sie erwähnen.Sie erstellen die Sammlung aus Ihrem ui-Thread und verwenden dann die Sammlung aus einem beliebigen Thread.

Verwandte Themen