2016-04-12 14 views
2

Ich habe eine WPF-Anwendung, die ihr Datenmodell innerhalb eines Timers aktualisiert, der seine Daten bei 1 Hz aktualisiert. Wie ich es verstehe, arbeitet ein Timer auf einem separaten Thread von der GUI. Alles scheint gut zu funktionieren, aber ich lese herum und sehe widersprüchliche Berichte darüber, ob es in Ordnung ist, Daten auf einem anderen Thread als dem GUI-Thread zu aktualisieren. Wir arbeiten mit .NET Framework 4 Client Profile. Der folgende Artikel sagt, die Dinge sind in 4.5 behoben, aber es ist mir immer noch nicht klar, dass es ist. Kann jemand das für mich klären? Wir verwenden noch keine Sammlungen in unserer Bindung. Deshalb haben wir keine Probleme.Ist es sicher, WPF-Steuerelemente an Daten zu binden, die in Timer aktualisiert werden?

WPF Databinding thread safety?

+0

Verwenden Sie einen DispatcherTimer. Das Tick-Ereignis wird im UI-Thread ausgelöst. – Clemens

Antwort

2

ja. Es ist Thread-sicher. INotifyPropertyChanged wird immer für UI-Threads von anderen Threads bereitgestellt.

Es ist nicht erforderlich, PropertyChanged von einem anderen Thread an den Benutzeroberflächenthread zu senden, da das Ereignis PropertyChanged automatisch an den UI-Dispatcher weitergeleitet wird.

Wie a MSDN article sagt:

Beachten Sie, dass in WPF, die Dinge anders, und der Code in Abbildung 5 funktioniert auch, wenn die Status-Eigenschaft wird auf einen Textblock dagetenbundenen. Diese ist, weil WPF das PropertyChanged-Ereignis im Gegensatz zu allen anderen XAML-Frameworks automatisch zu dem Hauptthread verschiebt. In allen anderen Frameworks wird eine Dispatcher-Lösung benötigt.

Allerdings ist es nur gilt für Änderungsbenachrichtigungen auf skalare Eigenschaften (das heißt PropertyChanged Ereignis). Sammlungsänderungsbenachrichtigungen (INotifyCollectionChanged.CollectionChanged Ereignis) funktionieren nicht auf diese Weise, sie müssen manuell im UI-Thread ausgelöst werden. Das heißt, wenn Sie INotifyCollectionChanged verwenden (z. B. mit einer ObservableCollection), werden diese Änderungen nicht zum UI-Thread gemarshallt. Dies bedeutet, dass Sie eine Ausnahme machen, wenn Sie die Sammlung aus einem nicht UI-Thread ändern. Zum Beispiel gibt es einige ViewModel Wir sind in der Klasse ViewModel und wir verwenden nicht den Dispatcher, um die Benutzeroberfläche zu aktualisieren. So rate ich Ihnen David Rickard Ansatz zu verwenden:

public static class DispatchService 
{ 
    public static void Invoke(Action action) 
    { 
     Dispatcher dispatchObject = Application.Current.Dispatcher; 
     if (dispatchObject == null || dispatchObject.CheckAccess()) 
    { 
      action(); 
     } 
     else 
     { 
      dispatchObject.Invoke(action); 
     } 
    } 
} 

und:

DispatchService.Invoke(() => 
{ 
    this.MyCollection.Add("new value"); 
}); 

David Rickard article at msdn blog.

Update:

ja, das article verwendet MVVMLight Rahmen. Es ist jedoch nicht korrekt, dass MVVM Light verwendet, um skalare Eigenschaft zu UI-Thread zu marshalieren. Es kann aus dem Quellcode ViewModelBase Klasse von MVVM Light, dass there is no marshal between threads to update scalar property zu sehen. Bitte, siehe RaisePropertuChanged() Methode.

Um auf Dispatching skalaren Eigenschaften jeden Zweifel zu zerstreuen ich einen Test gemacht haben:

XAML:

<TextBlock Text="{Binding Number}" FontSize="188" Foreground="Red" /> 

Ansichtsmodell:

public int Number { get; set; } 

private void UpdateNumber() 
{ 
     Task.Run(() => 
     { 
      System.Timers.Timer timer = new System.Timers.Timer(250); 
      timer.Elapsed += (sender, eventArgs) => 
      { 
       Number++; 
       OnPropertyChanged("Number");//No exceptions, no errors 
      }; 
      timer.Enabled = true; 
     }); 
} 

Update 1:

Es besteht kein Zweifel, dass INotifyProperyChanged Ereignis wird automatisch von WPF an UI-Thread gesendet. Ich glaube a MSDN article und die link hast du in deiner Frage gezeigt :).

Beachten Sie: Dies ist, weil WPF das PropertyChanged-Ereignis im Gegensatz zu allen anderen XAML-Frameworks automatisch an den Haupt-Thread sendet.

+0

Beachten Sie, dass der Artikel die MVVM Light-Frameworks verwendet, die zum UI-Thread wechseln. Also diese Antwort ist nicht für alle WPF-Anwendung –

+1

Sehr interessant. Vielen Dank. –

+0

@ErnodeWeerd, können Sie klären, was nicht für WPF funktioniert. Wir verwenden das MVVM Light-Framework nicht. – bsh152s

1

Nein, es ist nicht threadsicher. Eine gängige Methode zum Beheben dieses Problems besteht darin, im Benutzererweiterungs-Thread des geänderten Ereignisses notify property auf den UI-Thread umzuschalten.

Das 1000-fache Aktualisieren der Ansicht oder des Viewmodels ist im Allgemeinen nutzlos, weil die Anzeige nur 60 Mal pro Sekunde aktualisiert wird und der durchschnittliche Benutzer nicht in der Lage ist, tausend Werte pro Sekunde im Text zu lesen.

Derselbe Code, der das Benachrichtigungsereignis auslöst, könnte die Anzahl der Ereignisse pro Sekunde erhöhen.