3

Ok, im Anschluss an gestern habe ich eine neue Ebene der Komplexität hinzugefügt. Wir haben noch eine theoretische Model-Klasse, ViewModel und View. Dieses Mal mein Modell ein Threading.Timer hat (Ausgewählt wurde speziell Timer-Rückrufe auf der „falschen“ Thread zu bekommen.INotifyPropertyChanged, ObservableCollection, Threads und MVVM

Das Modell ein ObservableCollection hat. Die Timer-Rückruf Elemente in die Sammlung ergänzt.

Das Ansichtsmodell einfach geht die Sammlung der Ansicht, das ein Listenfeld auf die Sammlung gebunden enthält.

das funktioniert nicht.

das Modell macht auch eine Zeichenfolge, die in dem gleichen Timer-Rückruf aktualisiert wird.

Auch dies wird über das Viewmodel offengelegt und an eine TextBox gebunden.

Das funktioniert.

Ich habe Hinweise in meinem googling gesehen, dass das Aktualisieren von Sammlungen INotifyCollectionChanged nicht wie erwartet funktioniert. Ich bekomme eine totale Implosion, nicht einmal eine Ausnahme, nur die sofortige Beendigung des Antrags.

So gibt es zwei Fragen:

One gestern auf unsere Diskussion bezieht. Ich verwende INotifyPropertyChanged und ObservableCollections in meinem Modell, weil sie die Ansicht sind, auf der die Ansicht funktioniert. Es macht immer noch Sinn, diese Mechanismen zu verwenden, um mein Ansichtsmodell zu benachrichtigen, oder was auch immer das zugrunde liegende Modell geändert hat. Wie gehe ich mit Updates um, die auf einem anderen Thread stattfinden?

Zweitens, was passiert, dass INotifyPropertyChanged mit der Bindung arbeiten? Ich binde eine Zeichenfolgeneigenschaft an eine DependencyProperty namens Text, also ist es das DependencyProperty-System, das meine Änderung zurück zum UI-Thread leitet? Edit: Und kann ich mich darauf verlassen, d. H. Tut es das, weil sie erwarten, dass ich mit ihr einen Cross-Thread sprich, oder ist es nur ein Fang, auf den ich mich nicht verlassen sollte?

Die ListBox ist über ItemsSource = "{Binding ObsCollection}" gebunden. Wenn das die Anwendung abstürzt. Eigentlich erst begann ich mit der Zeit, wenn das Modell erstellt wurde, das geschah, als das Datacontext des Fensters gesetzt wurde, so wäre es eigentlich Visual Studio bombardieren ...

Dank

Antwort

1

WPF-Steuerelemente hat Thread-Affinität, was diese bedeutet, dass ihre Eigenschaften nur über den UI-Thread geändert werden können. Wenn Sie einen Eigenschaftswert von einem Timer (außer einem DispatcherTimer) aktualisieren, müssen Sie daher dieses Update auf dem UI-Thread marshalieren. Dies geschieht über den Dispatcher perfomed:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Normal, 
    new Action(() => // update your control here)); 

Die Datenbindung Rahmen stellt nicht sicher, dass Updates auf den UI-Thread gemarshallt wird deshalb, wenn Sie Ihr Modell aus einem anderen Thread aktualisieren, wird dies zu Problemen führen. Daher müssen Sie das gleiche Muster wie oben verwenden. Mit anderen Worten, wenn Sie Ihrer beobachtbaren Sammlung Einwände hinzufügen, muss diese Ergänzung über den Dispatcher durchgeführt werden.

+1

10 Das ist cool, aber was ist der "richtige" Prozess, um aus MVVM-Perspektive damit umzugehen? Ein Thread in meinem Modell aktualisiert also eine Sammlung. Sollte mein Model jetzt über Disponenten Bescheid wissen? Das scheint falsch zu sein. Was bedeutet, dass mein ViewModel schwerer wird, um mit der korrekten Trennung zwischen Model und View fertig zu werden. Ist das sinnvoll? – Ian

+1

Es gibt keinen "richtigen" Ansatz. Meine persönliche Präferenz besteht jedoch darin, eine einfache Schnittstelle zu erstellen, von der das ViewModel abhängt, IMarshalledInvoker, die eine einzige Methode zum Aufrufen einer Aktion in einem anderen Thread enthält. Wenn ich das ViewModel mit der View kuppel, erzeuge ich einen IMarshalledInvoker, der den Dispatcher unter den Covern verwendet. Mit diesem Muster kann ich noch Unit-Test, kann immer noch Designer-Unterstützung haben, d. H. Es ist gut MVVM ;-) – ColinE

+0

Ah ok, die Dinge kommen jetzt zusammen. Komischerweise gibt es verschiedene Wissensbereiche, die sich auf eine Art und Weise beziehen, die ich nicht berücksichtigt habe. Erstens, wir wissen, dass die Erstellung einer Thread-sicheren Sammlung schwierig ist. Ich hatte das vergessen und die Assoziation nicht in meinem Kopf gemacht ... Im Wesentlichen füge ich nur einen Thread hinzu. Lustig, wie du vergisst, warum du Sachen machst. Ich denke, der Kern der Antwort besteht darin, Änderungen von mehreren Threads in einen einzigen Thread zu trichterieren. Es gibt also einen einzigen Kanal zwischen der Ansicht und dem Viewmodel ?? – Ian

2

Dieses Problem ist ziemlich häufig in WPF. Ich denke, die beste Option ist Ihre eigene ObservableCollection<> Unterklasse, die sich darum kümmert, Ereignisbenachrichtigungen automatisch an den UI-Thread zu senden.

Und da das Rad bereits erfunden wurde, werde ich Sie einfach auf die Antwort auf diese Frage verweisen: ObservableCollection and threading.