2016-11-14 5 views
1

So versuche ich dynamische Daten-Display für WPF in meinem MVVM-Projekt Kaliburn (Ich benutze das LineChart-Steuerelement aus der Zukunft der dynamischen Datenanzeige, wenn jemand dies testen möchte Error). Es gibt ein LineChart, das an eine ObservableCollection bindet. Es funktioniert nur, wenn die Sammlung in dem Code-behing des Steuerelements vorgenommen wird, das über das LineChart verfügt. Wenn Sie versuchen, an eine Auflistung zu binden, löst die Abhängigkeitseigenschaft ViewModel eine InvalidOperationException aus. Wie kann dieses Problem gelöst werden?WPF Thread Binding Access Fehler beim Binden in MVVM

Ich habe gesehen, dass passiert, wenn Sie die Sammlung ändern, an die die Eigenschaft gebunden ist und Möglichkeiten, das zu beheben, aber nie in der tatsächlichen Prozess der Bindung. Ich habe versucht, die Erstellung der Sammlung in einen Dispatcher-Aufruf zu bringen (wie Sie es mit einem Hinzufügen oder Entfernen tun würden), aber es hat nicht geholfen.

Edit: Wie ich im zweiten Absatz erwähnt, ist die Ausnahme nicht an der Stelle der Änderung der Sammlung. Es wird zum Zeitpunkt der Bindung erhoben. Mehr noch, ich habe versucht, die Lösungen in der other question zu verwenden, und sie haben nicht geholfen.

Edit # 2: Die Ausnahmemeldung lautet "Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da ein anderer Thread es besitzt". Leute sagen mir immer wieder Lösungen zu Sammlungsänderungen, aber es kommt nicht einmal zu den Änderungen. Es schlägt bei der Bindungsstufe fehl (ItemsSource = "{binding collection}" in xaml).

Edit # 3: Ich habe überprüft und festgestellt, dass das ViewModel im UI-Thread erstellt wurde, die nur mehr Fragen gestellt.

+0

Mögliches Duplikat von [ObservableCollection und threading] (http://stackoverflow.com/questions/2293246/observablecollection-and-threading) – dymanoid

+1

Es würde wirklich helfen, wenn Sie die 'StackTrace' oder zumindest die' Nachricht' posten könnten Ihrer 'InvalidOperationException'. – haindl

+0

@haindl meine schlechte, fügte hinzu –

Antwort

2

Ok, es hat eine Weile gedauert, bis ich die Ursache des Problems gefunden habe.

Im Gegensatz zu dem, was andere vermutet haben, ist dies kein Multithreading-Problem.
Stattdessen ist es ein Problem mit der DynamicDataDisplay Bibliothek, die Sie verwenden.

Es gibt einen klaren Grund, warum Ihre ItemsSource Bindung Arbeiten auf Ihrem ListBox Objekt und nicht auf Chart funktionieren (vom Typ Microsoft.Research.DynamicDataDisplay.Markers2.LineChart):
Chart hat weder einen visuellen noch einen logischen Elternteil.

Sie können dies überprüfen, ob Sie den folgenden Code in Button_Click und gesetzt, nachdem sie einen Breakpoint einfügen:

var visualParent = VisualTreeHelper.GetParent(Chart); 
var logicalParent1 = Chart.Parent; 
var logicalParent2 = LogicalTreeHelper.GetParent(Chart); 

Sie sehen können, dass sie alle null sind.
So die Binding, die Sie auf LineChart.ItemsSourceProperty mit Path=ExampleCollection festgelegt haben, kann keinen Quellwert finden und weist null nur dem ItemsSource zu. Das liegt daran, dass die DataContext vom übergeordneten Element geerbt wird. Wenn jedoch kein übergeordnetes Element vorhanden ist, wird kein DataContext erben.
Und weil Chart nicht Teil des visuellen oder logischen Baumes ist, gibt es keine (einfache) Möglichkeit, eine Bindung an eine Außenseite DataContext kann sogar funktionieren.

Um sicherzustellen, dass die DataContext ist null gerade diese Linie zu dem vorhergehenden Code hinzu:

var dataContext = Chart.DataContext; 

Nun gibt es für diese drei mögliche Lösungen.
Erste, können Sie die DataContext von Window mit dem folgenden Code manuell erben können:

private void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
    // Just add the following line. 
    Chart.DataContext = DataContext; 

    Chart.StrokeThickness = 3; 
    Chart.SetBinding(LineChart.ItemsSourceProperty, new Binding("ExampleCollection")); 
    // ... 
} 

Wenn Sie diese eine Zeile einfach hinzufügen, werden Sie Ihre anderen multithreaded Code funktioniert ganz gut und das Diagramm sehen, dass wird mit einer Art Sinuswellenmuster aktualisiert.

Zweite, als eine weitere mögliche Lösung, die Sie in der Dokumentation der DynamicDataDisplay Bibliothek konsultieren und überprüfen Sie die bestimmungsgemäße Art und Weise verbindlich ein ItemsSource auf eine LineChart unter Verwendung von Daten zuzuweisen.
Ich habe selbst versucht, nach Dokumentation zu suchen und habe sogar zwei Stunden lang viel Code aus dieser Bibliothek getestet, aber die Dokumentation ist fast nicht vorhanden und der Code ist viel zu komplex, um ihn in ein paar Stunden vollständig zu verstehen. Ich habe versucht, mehrere Werkzeuge (Visual Studio Live Visual Tree, Snoop, ...) für die Anzeige der visuellen Baum der ChartPlotter, aber ich habe ein StackOverflowException jedes Mal, so im Wesentlichen etwas in dieser Bibliothek ist eine Art von Fehlern und Buggy.

Third, können Sie eine Resource als eine Art Proxy-Objekt verwenden, um eine „Bindungsbrücke“ auf die gleiche Instanz des MainWindowViewModel zu erstellen.
Damit dies funktioniert, müssen Sie hier vorgeschlagen, etwas zu tun wie: Data binding outside the visual tree. Data Context bridging

Unterm Strich: Also, wenn Sie nur die Arbeit erledigt erhalten möchten, würde ich die DataContext im Code festgelegt, wie oben gezeigt. (Vor allem, wenn die Instanz der ViewModel in der DataContext nie ändert.)
Wenn Sie reine Datenbindung verwenden möchten, würde ich wahrscheinlich die "binding bridge" oder suchen Sie nach einer anderen Charting-Bibliothek, die dieses Szenario unterstützt.

+0

Vielen Dank für die Hilfe und informative Antwort/Kommentare! –

+0

@PaulVinogradov Sie sind herzlich willkommen! Ich bin froh, dass ich dir helfen kann! :-) – haindl

0

Als THIS Thread-Status können Sie den UI-Dispatcher verwenden und die Funktion aufrufen, die Ihre ObservableCollection im UI-Thread ändert. Application.Current.Dispatcher sollte Ihnen den UI-Dispatcher geben. Wie die Lösung nahelegt, kann es im ViewModel stimmen. Eine sauberere und allgemeinere Lösung ist jedoch IMPLEMENT eine gleichzeitige und dennoch beobachtbare Sammlung würde es immer noch den Dispatcher speichern und die Änderungen auf der Benutzeroberfläche (oder angegebenen) Thread ausführen.