-4

(I use .Net 4.5, with Visual Studio 2017. Minimal example link has been added at the end of the post, and is ready to run/crash without anything to do if you want to see the things by yourselves)

I have a UserControl that contains a DataGrid in WPF. It's bound to a Viewmodel that contains a DataTable, AND a custom list of columns, in order to hide some of them when I can't delete them right at the moment for example.

DataGrid.Columns is readonly, so in the UserControl.DataContextChanged (I explain below why), I get the new DataContext, clear my grid.Columns collection and feed it with the custom list of DataGridColums I've read at load time.

Initially, I was doing a sync load. Everything was running fine (I'm loading many things, including a list of these Items). But I've put the loading into a backgroundworker (which ran almost fine) and, later, replaced the backgroundworker by an async loading using Task.Run().

All of the other lists of item in my window are well loaded, the code runs fine. BUT the moment I'm trying to replace mygrid.Columns by other ones, I have a InvalidOperationException saying that the calling thread can't access this object because another thread owns it.

I've tried many things, many BeginInvoke from myGrid.Dispatcher, App's current Dispatcher and so on, and every piece of async code I found, but I cannot figure out how to add a simple item to this ObservableCollection.

I've seen custom asyncObservableCollection but can't use them, DataGrid.Columns being read-only.

I'm puzzled by the fact that I thought UserControl_DataContextChanged was belonging to the UI thread, so it should be able to change user controls safely.

I've updloaded a "minimal example" here => https://files.fm/u/k2srba6m The problem is in ItemViewmodel.FilterColumns, as my original code does (hope the solution to this will work on the original code too)

Any help would be appreciated (and sorry for my english)

Poc
  • 109
  • 10
  • 2
    We are software people here and better understand code than a belletristic novel you have posted here... Please provide a [mcve] if you want a quick and precise answer. – dymanoid Oct 18 '17 at 08:31
  • I uploaded a "minimal, complete and verifiable example" (but could not write the whole code in my post) so that you could give me your solution. Thanks in advance. – Poc Oct 18 '17 at 10:28
  • -4 like if I hadn't done any research effort or tried to explain it clearly, while I'm on it for 4 days, nice... – Poc Oct 18 '17 at 10:41
  • This is a very common issue in multi-threaded WPF applications. So there are plenty of help to find. The issue is that you haven't supplied the key things that is needed to help you. For example the .Net version or simple code snippets. – Deantwo Oct 18 '17 at 11:16
  • Actually there is, I provided the minimal example you asked for in the edited post. I'm going to add the FW version right now. – Poc Oct 18 '17 at 11:42

2 Answers2

0

If you are using .Net 4.5 or later you can try using BindingOperations.EnableCollectionSynchronization.

In your window constructor:

// Sync collection with UI
BindingOperations.CollectionRegistering += BindingOperations_CollectionRegistering;

And add this method:

/// <summary>
/// Handles the CollectionRegistering event of the BindingOperations control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="CollectionRegisteringEventArgs"/> instance containing the event data.</param>
private void BindingOperations_CollectionRegistering(object sender, CollectionRegisteringEventArgs e)
{
    BindingOperations.EnableCollectionSynchronization(e.Collection, e.Collection);
}

This will automatically call the EnableCollectionSynchronization method whenever an ObservableCollection is created.

I set it up to simply use the collection itself as the lockObject, don't know if that is a bad idea, but it worked on my end.


If you are using .Net 4.0, you might have to get the AsyncObservableCollection to work. I have not found another way to do this in .Net 4.0.

Deantwo
  • 1,113
  • 11
  • 18
  • Doesn't work :( I updloaded a "minimal example" here => https://files.fm/u/k2srba6m This is a simplified code but it fails in ItemViewmodel.FilterColumns, as my original code does. – Poc Oct 18 '17 at 10:22
  • You didn't mention what didn't work or what you tried. I don't have the time to download your code and test it. – Deantwo Oct 18 '17 at 11:02
  • I was asked by previous commenter to provide some code, that's why I uploaded it. What does not work: my code, in ItemViewmodel.FilterColumns, when I try to add an item to the collection, just as before. Same error. InvalidOperationException. Thank you anyway for having tried to help me :) – Poc Oct 18 '17 at 11:12
0

I found an answer thanks to help on another forum. I leave the question here because answer was hard to find.

Adding to DataGrid.Columns a DataGridColumn instanciated by hand in the viewmodel was faulty, the instanciation itself should have been invoked in the UI thread. The OC.Add threw an exception but mainly because of the argument (and in some cases the call itself, yes).

Sorry, again, no quotes full of code, just "belletristic novel", but it could help.

Poc
  • 109
  • 10