Verfasst von: bletra | 29. September 2012

Multithreading in WPF (TPL, MVVM): Bindbare Collections (Teil 4 von 5)

Mit Tasks können relativ einfach Aktionen im UI-Thread oder im Hintergrund ausgeführt werden. Typischerweise verwendet man ObservableCollections, um Daten eines ViewModels an eine Listbox zu binden. ObservableCollections sind jedoch nicht thread-safe, d.h. man kann nicht einfach von einem Hintergrundthread Elemente in eine Liste einfügen, die im UI-Thread erzeugt wurde. Beim Füllen einer Collection des ViewModels mit Bilder oder Informationen eines Webservices oder des Webs allgemein, wäre es praktisch, einfach auf die Collection zugreifen zu können, ohne sich darüber Gedanken zu machen, in welchem Thread man ist. Seit .Net 4.0 gibt es thread-sichere Listen, die genau das bieten.
Keine davon eignet sich jedoch zur direkten Datenbindung, da sie Änderungen nicht mitteilen. Microsoft stellt tolle Beispielprojekte zur Verfügung, die verschiedene parallele Mechanismen von .Net demonstrieren: Samples for Parallel Programming with the .NET Framework. Darin findet sich auch eine Bibliothek (ParallelExtensionsExtras) mit einer thread-sichere beobachtbare Liste: ObservableConcurrentCollection. Die Klasse kümmert sich selbst darum, die GUI-Controls, die auf Ereignisse lauschen im richtigen Thread darüber zu informieren. Verwendung in unserem Kontext im ViewModel:

//Klassen-Member
private IProducerConsumerCollection<ImageInformation> items;

//Command mit Hintergrundprozess
Task.Factory.StartNew(() =>
{
   //irgendein zeitaufwändiges Erstellen/Suchen eines Items -> item
   //direktes Hinzufügen ohne StartNew und uiScheduler:
   while (!items.TryAdd(item))
      System.Threading.Thread.Sleep(100); //add hat nicht geklappt, also warten und neu versuchen
   System.Threading.Thread.Sleep(500); //UI-Thread Zeit lassen, auf die Änderungen zu reagieren
}, CancellationToken.None);

Im konkreten Fall, der Iteration über viele Bilder, die in die Liste eingefügt werden funktionierte alles so lange gut, so lange ich mit Thread.Sleep(500); nach jedem Iterationsschritt gearbeitet habe. Beim Hinzufügen von 1000 Bildern werden jedoch 3000 Events mit Property-Änderungen im UI-Thread eingefügt und gefeuert. Dies ist anscheinend zu viel und führt zu Problemen.
Ich suche also nach einer Liste, die sich zur Datenbindung eignet, jedoch nur auf Aufforderung ein entsprechendes Event für die GUI feuert. Hierzu eignet sich folgende simple Eigenimplementation:

public class ExplicitlyObservableList<T> : List<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
   public event NotifyCollectionChangedEventHandler CollectionChanged;
   public event PropertyChangedEventHandler PropertyChanged;
   protected void NotifyPropertyChanged(string propertyName)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
   public void Notify()
   {
      NotifyPropertyChanged("Count");
      NotifyPropertyChanged("Item[]");
      if (CollectionChanged != null)
      {
         CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); //lädt alles neu, nicht nur letzte Änderungen!
      }
   }
}

Noch besser wäre es, diesen Ansatz mit IProducerConsumerCollection zu kombinieren. Da ich jedoch TryAdd auch etwas mühsam finde, reicht mir die vereinfachte Implementation und ich werde mir eben doch klar darüber, in welchem Thread ich was tue.

Im letzten Artikel werde ich die Code-Fragmente zu einer fertigen Applikation zusammenführen.Teil 1: Einleitung


Antworten

  1. […] habe, erweitert. Das ViewModel verwendet die Klasse ExplicitlyObservableList, wie in Artikel “Multithreading in WPF (TPL, MVVM): Bindbare Collections (Teil 4 von 5)” […]

  2. […] kill, Task « Multithreading in WPF (TPL, MVVM): Tasks (Teil 2 von 5) Multithreading in WPF (TPL, MVVM): Bindbare Collections (Teil 4 von 5) […]

  3. […] Multithreading in WPF (TPL, MVVM): Bindbare Collections (Teil 4 von 5) […]

  4. […] Multithreading in WPF (TPL, MVVM): Bindbare Collections (Teil 4 von 5) […]


Hinterlasse einen Kommentar

Kategorien