Gute Abend 😃,
ich würde gerne eure Meinung zu diesen zwei Varianten hören, welche das DataBinding ThreadSafe machen sollen.
Oder ist das eine völlig falsche Herangehensweise an diesem Problem?
Variante 1:
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged([CallerMemberName] String propertyName = null)
{
PropertyChangedEventHandler eventHandler = PropertyChanged;
if (eventHandler != null)
{
Delegate[] delegates = eventHandler.GetInvocationList();
foreach (PropertyChangedEventHandler handler in delegates)
{
DispatcherObject DispatcherObject = handler.Target as DispatcherObject;
if (DispatcherObject != null && DispatcherObject.CheckAccess() == false)
{
DispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, new PropertyChangedEventArgs(propertyName));
}
else
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Variante 2:
public Dispatcher DispatcherObject { get; set; } = Dispatcher.CurrentDispatcher;
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged([CallerMemberName] String propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null && DispatcherObject != null)
{
if (DispatcherObject.CheckAccess())
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
else
{
DispatcherObject.Invoke(() => handler(this, new PropertyChangedEventArgs(propertyName)));
}
}
}
Grüße
generell würde ich empfehlen, langlaufende Vorgänge vom Gui abzutrennen.
Also wenn in einer Schleife 10000 Werte berechnet werden, ists nicht sinnvoll, jedes einzelne Ergebnis via Databinding+thread-Delegation ans Gui zu melden - solch frisst ungeheuer Performance, und so schnell gucken kann auch niemand.
Hingegen in Einzelfällen mags auch mal sinnvoll sein, threadsicheres Databinding zu implementieren.
Ich würde aber eine effizientere Variante wählen, das mit dem Auspopeln der InvokationList, und dann jeden Abbonenten einzeln und mit Thread-Delegation invoken - das ist ziemlich lahm.
Der frühe Apfel fängt den Wurm.
Halte ich für völlig falsch.
Schau dir mal Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding an. Ich denke das ist es was du suchst.
Was hälst du für völlig falsch?
Und warum - kannst du es vlt. kurz in eigenen Worten sagen?
Der frühe Apfel fängt den Wurm.
Ja, das ViewModel stellt die Daten für die View und die läuft im UI Thread somit sollten die Eigenschaften im ViewModel ausschließlich im gleichen Thread gefüllt/verändert werden wie die zugehörige View.
Es gibt hinreichend Klassen/Verfahren um dieses schnell und einfach zu erreichen (async/await oder IProgress).
Die Frage ist also eher warum man sich den Stress ins ViewModel hineinholen sollte. Ich betrachte das ViewModel wie eine Abstraktion der View und behandle die Eigenschaften/Events so als ob ich die View direkt anfassen würde und dort arbeite ich in der Ebene auch immer im UI Thread.
WPF macht es nichts aus, wenn PropertyChanged
auf einem Hintergrundthread ausgelöst wird. Gebundene Controls werden automatisch auf dem UI-Thread geupdated. Ich finde jetzt leider keine offizielle Dokumentation darüber, aber ich kann bestätigen, dass es funktioniert. Siehe auch diesen StackOverflow Thread.
Man muss aber mit der ObservableCollection<T>
-Klasse aufpassen, da muss man auf den UI-Thread, bevor man irgendwas damit macht.
WPF macht es nichts aus, wenn
PropertyChanged
auf einem Hintergrundthread ausgelöst wird. Das funktioniert generell für Aufzählungen nicht. Bei List<T> und Kollegen ist das sogar gefährlich weil es manchmal funktioniert.
Kommt auf das Timing an.
Das sollte funktionieren:
var list = new List<>();
list.Add(...);
vm.Collection = list;
Das kann in die Hose gehen:
var list = new List<>();
vm.Collection = list;
list.Add(...);
Dieses kann man aber ganz simpel per async/await lösen oder mit dem Konstrukt aus meinem Link.
Für mich ist das Schreiben der Properties in irgendeinem Hintergrund Thread keine Option.
Erst einmal vielen Dank für das ganze Feedback 😃
Der oben angegebene Code-Schnipsel war für das jeweilige ViewModel vorgesehen.
Dieses Model sieht dann nicht vor, dass eine 'infinity' Task Properties verändern kann/sollte?
Dieser "infinity" Task würde ja im Model/Service laufen. Das ViewModel hängt sich dann als Subscriber da rein und verändert die Eigenschaft synchronisiert im passenden Thread Kontext.
Nur so hat man auch die Chance dort noch regulatorisch einzugreifen (wenn z.B. zu viele Änderungen pro Sekunde dort reinlaufen, dann übernimmt man nicht jeden Wert in die Anzeige, sondern nur maximal 10 Änderungen pro Sekunde).
Dieser "infinity" Task würde ja im Model/Service laufen. Das ViewModel hängt sich dann als Subscriber da rein und verändert die Eigenschaft synchronisiert im passenden Thread Kontext.
Nur so hat man auch die Chance dort noch regulatorisch einzugreifen (wenn z.B. zu viele Änderungen pro Sekunde dort reinlaufen, dann übernimmt man nicht jeden Wert in die Anzeige, sondern nur maximal 10 Änderungen pro Sekunde).
Der Task läuft im Model, das ist richtig.
Wie ist das mit dem Subscriber gemeint?
Der Task oder die Aufgabe rödelt im Hintergrund herum und posaunt die gewonnene Erkenntnis in die Welt hinaus.
Das geht z.B. mit einem EventAggregator.
Jeder der sich für diese Events interessiert meldet sich an dem EventAggregator an (subscribe) und wird von nun an benachrichtigt. Bei einer geschickten Implementierung kann man beim Anmelden auch gleich den gewünschten Thread Kontext für diese Benachrichtigung auswählen/festlegen.