1

So I am trying to incorporate dynamic-data-display for WPF into my MVVM caliburn project (I am using the LineChart control from future of dynamic data display, if anyone wishes to test this error). There is a LineChart that binds to an ObservableCollection. It only works if the collection is made in the code-behing of the control which has the LineChart. If you try to bind to a collection the ViewModel the dependency property raises an InvalidOperationException. How can this problem be solved?

I have seen that happen when you change the collection which the property is binded to and know ways to fix that, but never in the actuall proccess of binding. I've tried putting the creation of the collection in a dispatcher invoke (like you wold do with an add or remove) but it didn't help.

Edit: As I stated in the second paragraph the exception is NOT at the point of changing the collection. It is being raised at the point of binding. Even more, I have tried to use the solutions in the other question and they did not help.

Edit #2: The exception message is "The calling thread cannot access this object because a different thread owns it". People keep telling me solutions to collection changes but it doesn't even get to the changes. It fails at binding stage (ItemsSource="{binding collection}" in xaml).

Edit #3: I double-checked and noticed that the ViewModel is created in the UI thread which only made more questions.

Community
  • 1
  • 1
  • Possible duplicate of [ObservableCollection and threading](http://stackoverflow.com/questions/2293246/observablecollection-and-threading) – dymanoid Nov 14 '16 at 14:36
  • 1
    It would really help if you could post the `StackTrace` or at least the `Message` of your `InvalidOperationException`. – haindl Nov 14 '16 at 15:46
  • @haindl my bad, added it – Paul Vinogradov Nov 14 '16 at 16:17
  • 1
    Much better. It seems that you have some code that assigns the `ItemsSource` property of some UI element outside of the dispatcher thread. More importantly, this is definitely an assignment done in C# and not in XAML code. So it's not a data binding but a manual property assignment. Can you post line 75 of your `MainWindow.xaml.cs`? Or maybe even the whole method `gogogo()` and the starting of the thread? It shouldn't be hard to find the cause of the exception. – haindl Nov 14 '16 at 16:49
  • @haindl this is just an assignment out of another thread where I had recreated this problem (in the original dll it's not mvvm but mvm). I have this same exception in my project when I set the source out of XAML. (sorry for throwing you off a bit). I can't really get any good diagnostics from the XAML binding project. I can post my code to a github rep, it'll be easier rather than explaining it here. https://github.com/Imhotebkin/BindingException/tree/master/GraphTesting here. The error is in the MainWindowView and MainWindowViewModel – Paul Vinogradov Nov 14 '16 at 16:58
  • 1
    I checked your code but I'm sorry to say that it is completely unusable for me. First, it doesn't compile. (That's bad.) And second, it's not clear to me where exactly the exception happens. You have a lot of code and commented code there. I can't just guess what you mean and where it happens. So please cleanup your code, delete the commented code, show exactly the line where the exception happens, make it compilable and then reupload again. (This is called a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).) As soon as you reupload, I'll take another look at it. – haindl Nov 14 '16 at 18:10
  • 1
    And "minimal" also means that you delete all the code that has nothing to do with your exception. For example, I don't know what `SynchronizedObservableCollection` has to do with it and why `TouchView` even exists. – haindl Nov 14 '16 at 18:22
  • The OC has to be created on the UI thread, and when you update it you must do so on the UI thread. The Dispatcher helps with the second task, creating your view models in the UI thread helps with the first. –  Nov 14 '16 at 19:10
  • @haindl okay so I cleaned up the project more and wrote comments to everything I thought might be unclear. I think it didn't compile on your computer because you didn't do Nuget Package Restore (VS did it automatically when I cloned the project in it). It compiled on my home computer from the get-go. – Paul Vinogradov Nov 14 '16 at 20:29

2 Answers2

2

Ok, it took me quite a while to find the root cause of your problem.

Unlike from what others suspected, this isn't a multithreading issue at all.
Instead it's an issue with the DynamicDataDisplay library you are using.

There's a clear reason why your ItemsSource binding works on your ListBox object and doesn't work on Chart (of type Microsoft.Research.DynamicDataDisplay.Markers2.LineChart) :
Chart neither has a visual nor a logical parent.

You can check this if you insert the following code into Button_Click and set a breakpoint after them:

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

You can see that they are all null.
So the Binding that you set on LineChart.ItemsSourceProperty with Path=ExampleCollection cannot find any source value and just assigns null to the ItemsSource. That's because the DataContext is inherited from the parent - but when there isn't any parent then there isn't any DataContext to inherit either.
And because Chart isn't part of the visual or logical tree, there is no (easy) way any binding to an outside DataContext can even work.

To verify that the DataContext is null just add this line to the preceding code:

var dataContext = Chart.DataContext;

Now there are three possible solutions for this.
First, you can manually inherit the DataContext from Window using the following code:

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"));
    // ...
}

If you simply add this one line, you'll see that your other multithreaded code is working just fine and the chart is updated with some kind of sine wave pattern.

Second, as another possible solution you can consult the documentation for the DynamicDataDisplay library and check the correct and intended way to assign an ItemsSource to a LineChart using data binding.
I tried searching for documentation myself and even debugged a lot of code from that library for two hours or so, but the documentation is nearly non-existent and the code is far too complex to fully understand it in a few hours. I tried using several tools (Visual Studio Live Visual Tree, Snoop, ...) for displaying the visual tree of the ChartPlotter but I got a StackOverflowException every time, so essentially something in this library is kind of flawed and buggy.

Third, you can use a Resource as a kind of proxy object to create a "binding bridge" to the same instance of the MainWindowViewModel.
For this to work, you have to do something like suggested here: Data binding outside the visual tree. Data Context bridging

Bottom line: So, if you just want to get the job done, I would set the DataContext in code like shown above. (Especially if the instance of the ViewModel in the DataContext never changes.)
If you want to use pure data binding then I would probably use the "binding bridge" or search for another charting library that supports this scenario.

Community
  • 1
  • 1
haindl
  • 3,111
  • 2
  • 25
  • 31
0

As THIS thread states you can use the UI dispatcher and invoke the function that changes your ObservableCollection on the UI thread. Application.Current.Dispatcher should give you the UI dispatcher. As the solution suggests it can be right in the ViewModel. However, a cleaner and more generic solution is to IMPLEMENT a concurrent and still observable collection it would still store the dispatcher and run the changes on the UI (or specified) thread.

Community
  • 1
  • 1
Daniel Leiszen
  • 1,827
  • 20
  • 39