This Type of Collectionview Does Not Support Changes to Its Sourcecollection from a Thread Different from the Dispatcher Thread

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread

Since your ObservableCollection is created on UI thread, you can only modify it from UI thread and not from other threads. This is termed as thread affinity.

If you ever need to update objects created on UI thread from different thread, simply put the delegate on UI Dispatcher and that will do work for you delegating it to UI thread. This will work -

    public void Load()
{
matchList = new List<GetMatchDetailsDC>();
matchList = proxy.GetMatch().ToList();

foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList)
{
App.Current.Dispatcher.Invoke((Action)delegate // <--- HERE
{
_matchObsCollection.Add(match);
});
}
}

CollectionView does not support changes to its source collection from a thread different from the dispatcher thread - caused from dispatcher thread

In my case the problem was that the collection view was refreshed in a task. Then later adding to the collection from the Main UI thread caused an exception.

When constructing the view model the collection was refreshed in a delayed task.

public MainVM()
{
//other code...
Task.Delay(100).ContinueWith(_ => UpdatePreferences());
}

public void UpdatePreferences()
{
//other code..
CollectionViewSource.GetDefaultView(Data.Customers).Refresh();
}

I was able to fix the problem by invoking the dispatcher.

Task.Delay(100).ContinueWith(_ => App.Current.Dispatcher.Invoke(()=> UpdatePreferences()));

Error: This type of CollectionView does not support changes to its SourceCollection

Have you tried wrapping all your code which sets up the filter to a call to the WPF dispatcher?

Generally this has to be done if the collection bound to the view is modified in code of a thread different than the UI thread.

Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
HeroesDBAddHeroes();
}));

Updating CollectionView in a different thread

I finally what it was working. It was pretty simple.

public class ConsultInvoiceViewModel : ViewModelBase
{

public Context ctx = new Context();

private ICollectionView _dataGridCollection;
private ObservableCollection<Invoice> invoiceCollection;

public ConsultInvoiceViewModel()
{
invoiceCollection = new ObservableCollection<Invoice>();
DataGridCollection = CollectionViewSource.GetDefaultView(Get());

if (!WPFHelper.IsInDesignMode)
{
var tsk = Task.Factory.StartNew(InitialStart);
tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}
}

private void InitialStart()
{
try
{
State = StateEnum.Busy;
Get();
}
finally
{
State = StateEnum.Idle;
}
}

private void SearchFilter()
{

Task tsk = Task.Factory.StartNew(()=>
{
try
{
State = StateEnum.Busy;
using (var ctx = new Context())
{

var invs = ctx.Invoices
.Where(s.supplier == 1)
.GroupBy(x => new { x.suppInvNumber, x.foodSupplier })
.ToList()
.Select(i => new Invoice
{
suppInvNumber = i.Key.suppInvNumber,
foodSupplier = i.Key.foodSupplier,
totalPrice = i.Sum(t => t.totalPrice),
});
.

App.Current.Dispatcher.Invoke((Action)delegate
{
invoiceCollection.Clear();
if (invs != null)
foreach (var inv in invs)
{
invoiceCollection.Add(inv);
}
});

}
}
finally
{
State = StateEnum.Idle;
}

});
tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}

private ObservableCollection<Invoice> Get()
{

using (var ctx = new Context())
{
var invs = ctx.Invoices

foreach (var inv in invs)
{
App.Current.Dispatcher.Invoke((Action)delegate
{
invoiceCollection.Add(inv);
});
}
}

Xamarin Forms Android This type of collectionView does not support changes to its sourceCollection from a thread different from the dispatcher thread

I saw the same problem using VS2017 15.9.9. Even the same code that could be archived a week ago threw the above mentioned error. It seems related to local data in the solution. A "clear" build was not sufficient. However manual deletion of the local obj and bin folders followed by archiving (build is done implicitly) solved the problem for me.

mvvm backgroundworker - CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread

I modified my code as follows and it works the second time through now.

    void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ScriptModel _newFileResult = new ScriptModel();
ObservableCollection<ScriptModel> _scriptCollectionTemp = new ObservableCollection<ScriptModel>();

for (int i = 0; i < ScriptCollection.Count; i++ )
{
_newFileResult = CheckOutResults.CheckOutResultFiles(ScriptCollection[i], BatteryLocation, SelectedMachine.Machine);
_scriptCollectionTemp.Add(_newFileResult);
worker.ReportProgress(_scriptCollectionTemp.Count);
}
ScriptCollection = _scriptCollectionTemp;
}

Threading and creating ObservableCollection

The problem isn't actually the ObservableCollection<Something> at all, it's the <ItemsControl ItemsSource="{Binding Settings.collection}".

ObservableCollection itself doesn't have any problem being accessed across multiple threads (as long as those threads are accessing it one at a time). The same goes for normal (non-DependencyProperty) properties- they don't inheritly belond to a single thread. The WPF binding system can (for the most part) handle changes to bound properties from other threads (provided you aren't using DependencyProperty). What can't handle changes from other threads, is ItemsControl.

Let's step through it, but first I have to make a few assumptions. First, I have to assume that collection is a actually a property, not a field (or is at least exposed via a property), since you can't bind to a field. Second, I'll have to assume that the INotifyPropertyChanged interface is involved, probably implemented on the Settings class.

So stepping through:

  1. In Settings.Modify, you execute collection = new ObservableCollection<Something>();. This successfully updates the property. The WPF binding system gets notified of this change (through the presumed INotifyPropertyChanged interface) and handles the thread change for you. So ItemsControl.ItemsSource gets updated on the UI thread, via the binding, to the new value of collection.
  2. ItemsControl takes this new ObservableCollection and creates a CollectionView which it uses as the data source for its items.
  3. Back in Settings.Modify, you call collection.Add(...). This successfully adds an item to the ObservableCollection<Something>, which triggers its INotifyCollectionChanged.CollectionChanged event.
  4. The CollectionView which the ItemsControl created, handles the CollectionChanged event, but still on the other thread, where it was raised. The CollectionView, as the exception says, doesn't "support changes to its SourceCollection from a thread different from the Dispatcher thread". So it throws an exception. This exception looks like it's coming from collection.Add, but if you look in the call stack, you'll see it actually comes from many frames deeper. collection.Add is just the deepest level of your code that is involved.

When working with ObservableCollections and multiple threads, I would advise creating the full collection on the background thread (if possible), before passing the full collection back to the UI thread for binding. In your example, you could create a local variable, add your items to that, then set it to your collection property once all the items are in place. Alternatively, you could pass individual items back to the UI thread to be added, using Dispatcher.Invoke or a Task<Something>.



Related Topics



Leave a reply



Submit