XAML-Kolumne: WPF, Silverlight, Windows Phone 8 und Windows-Store-App

WPF-Performancetipp Teil 2: „ObservableCollection“ synchronisieren
Keine Kommentare

In der Kolumne „XAML Expertise“ präsentiert Gregor Biswanger Top-How-tos zum Thema XAML. Einsteiger und fortgeschrittene XAML-Experten sollen hier durch geballtes Wissen gesättigt werden. Heute gibt es folgende Expertise: „WPF-Performancetipp: die SpeedObservableCollection“, „WPF-Performancetipp: ObservableCollection performanter threadübergreifend synchronisieren“ und „XAML: XAML Designer ohne Code laden“.

Für einen threadübergreifenden Zugriff auf die ObservableCollection wird häufig der Application.Current.Dispatcher eingesetzt. Für kleine Datenmengen wird das auch ohne schwerwiegende Auswirkungen funktionieren. Bei großen Daten hingegen könnte das die Benutzeroberfläche einfrieren lassen.

„ObservableCollection“ threadübergreifend synchronisieren

Die erste Ursache bei einer höheren Menge an Daten ist das permanente Auslösen vom Render-Vorgang, wie es bereits beim ersten How-to zur SpeedObservableCollection beschrieben und gelöst wurde. Abgesehen davon gibt es noch weitere Hürden, wenn die Datensynchronisierung zum UI-Thread stattfindet. In Listing 1 werden 200 000 Daten geladen. Beim Ausführen der Anwendung kann das Fenster beim Befüllen einer ListView nicht mehr bewegt und bedient werden.

Listing 1: Ein Test mit 200 000 Daten lässt die Oberfläche einfrieren

public class MainWindowViewModel
{
  public ObservableCollection SampleData { get; set; }

  public MainWindowViewModel()
  {
    SampleData = new ObservableCollection();
    Task.Run(() => GenerateData());
  }

  private void GenerateData()
  {
    for (int i = 0; i < 200000; i++)     {       Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
          {
          SampleData.Add(DateTime.Now.ToLongTimeString());
      }));
    }
  }
}

Der Application.Current.Dispatcher blockiert die Oberfläche bei der Synchronisierung so lange, bis der komplette Datensatz überreicht wurde. Eine kleine Notlösung hierbei wäre der zusätzliche Einsatz von Async-Await. In Listing 2 wird dieser eingesetzt, und es wird demonstriert, dass die Anwendung normal startet und für einen kurzen Augenblick bedient werden kann. Dann ist es aber auch für die Async-Await-Lösung zu viel und die Oberfläche lässt sich kaum noch bedienen.

Listing 2: Eine Async-Await-Lösung hilft nur bedingt

private async void GenerateData()
{
  for (int i = 0; i < 200000; i++)   {     await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
    {
      SampleData.Add(DateTime.Now.ToLongTimeString());
    }));
  }
}

Die schnellste und somit die beste Lösung bietet die CollectionSynchronization, die neu ab WPF 4.5 dazu gekommen ist. Sie sorgt dafür, dass eine performante Synchronisierung zum UI-Thread stattfindet, ohne dabei die Oberfläche auszubremsen. Selbst wenn die eigene WPF-Anwendung keine 200 000 Daten laden muss, wird die eigene Anwendung im Allgemeinen spürbar flüssiger wirken, und es wird empfohlen, den Dispatcher durch eine CollectionSynchronization zu ersetzen. Listing 3 zeigt, wie diese eingesetzt wird.

Listing 3: Die „Collection Synchronization“ synchronisiert am schnellsten und bremst die Oberfläche nicht aus

 public class MainWindowViewModel
{
  public ObservableCollection SampleData { get; set; }

  private object _listLock = new object();

  public MainWindowViewModel()
  {
    SampleData = new ObservableCollection();
    BindingOperations.EnableCollectionSynchronization(SampleData, _listLock);

    Task.Run(() => GenerateData());
  }

  private async void GenerateData()
  {
    for (int i = 0; i < 200000; i++)
    {
      lock (_listLock)
      {
        SampleData.Add(DateTime.Now.ToLongTimeString());
      }
    }
  }
}

Aufmacherbild: Microphone in a recording studio von Shutterstock / Urheberrecht: Stock image

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -