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));
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); }
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
- Diagnosing a WPF Process That Stays Alive After the Window Closes — ShutdownMode and Foreground Threads
- WPF ComboBox ItemsSource Binding Patterns and Selected Value Retrieval
- How to Prevent SelectedItems from Appearing Lost in a Virtualized WPF ListBox
- Releasing the Image File Locked by BitmapImage in WPF with BitmapCacheOption.OnLoad
- How to Reset DataGrid Sorting in WPF
- How to Implement DataGrid Sorting in WPF
- DataGrid | WPF Standard Control Demo App (.NET 10)
- Why Task.Result Freezes a WPF App and When ConfigureAwait(false) Does Not Help
Top comments (2)
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.