DEV Community

s-iguchi09
s-iguchi09

Posted on Edited on Originally published at s-iguchi09.github.io AI-assisted

Fixing the Cross-Thread Exception When Updating an ObservableCollection in WPF

You load data inside Task.Run and call Items.Add(line) on an ObservableCollection<T> bound to a ListBox. It throws NotSupportedException: this type of CollectionView does not support changes to its SourceCollection from a thread other than the Dispatcher thread.

This is a short summary. The full article, with a bound vs unbound comparison and a 5,000-item timing test on .NET 10, is here:
Fixing the Cross-Thread Exception When Updating an ObservableCollection in WPF

The collection is not what throws

The same Add on an unbound collection did not throw. The exception comes from the CollectionView that WPF puts between the collection and the control, which belongs to the UI thread. That does not make the collection thread-safe. It only tells you where the check lives.

Two fixes

Few, occasional updates: move the change onto the UI thread.

dispatcher.Invoke(() => Items.Add(line));
Enter fullscreen mode Exit fullscreen mode

Many, frequent updates: register a lock once on the UI thread, then take that same lock for every access.

BindingOperations.EnableCollectionSynchronization(Items, _lock);
// on the worker
lock (_lock) { Items.Add(line); }
Enter fullscreen mode Exit fullscreen mode

Adding 5,000 items one by one, the Dispatcher.Invoke loop took 992 ms. With the shared lock the loop took 6 ms, and the view had all 5,000 notifications after 64 ms. The UI thread still applies them, just later.

Controls themselves stay UI-thread-only either way.

A Japanese version is also available.

Related Articles

Top comments (2)

Collapse
 
raknaos profile image
Raknaos •

For anyone hitting this at high update rates: EnableCollectionSynchronization fixed the exception for us too, but we then saw UI stutter under bursts because every SynchronizationContext post serialized on the same lock. Worth mentioning whether you batched the deltas (coalescing bursts into periodic UI-safe updates) or moved to an incremental source instead — that tradeoff is where most implementations quietly fall over.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.