Исключение возникает из-за сродства потока в WPF , что является преднамеренным.
И ItemsControl, и CollectionView имеют сходство с потоком, в котором был создан ItemsControl , что означает, что их использование в другом потоке запрещено и вызывает исключение.
Когда ваша коллекция изменяется, она вызывает событие CollectionChanged
, которое будет обрабатываться элементом управления, связывающим ObservableCollection
. Затем этот элемент управления обновит свои элементы в том же потоке. В вашем примере это не поток пользовательского интерфейса, а ваш рабочий поток.
Решение операций привязки
Ваш подход с использованием BindingOperations.EnableCollectionSynchronization
доступен с. NET Framework 4.5 и совершенно допустим, но вы должны убедиться, что нет взаимоблокировок с выбранным вами механизмом синхронизации, что может быть сложно. Я рекомендую вам проверить ссылку для получения подробной информации об этом.
Диспетчерское решение
Более общий способ решения этой проблемы - делегировать добавление элементов в поток пользовательского интерфейса .
public void HandleNewOffers(object sender, ICollection<Offer> newOffers)
{
// The same as you do, but simplified with Linq to return a filtered enumerable
var filteredOffers = newOffers.Where(no => !_currentOffers.Any(co => co.Id == no.Id)).Select(MapOffer);
// Use this to invoke the add method synchronously on the UI thread
System.Windows.Application.Current.Dispatcher.Invoke(() => AddToCurrentOffers(filteredOffers));
// Use this to alternatively invoke the add method asynchronously on the UI thread
System.Windows.Application.Current.Dispatcher.InvokeAsync(() => AddToCurrentOffers(filteredOffers));
}
// Just a helper method that can also be inlined as lambda
private void AddToCurrentOffers(IEnumerable<Offer> offers)
{
foreach (var offer in offers)
{
_currentOffers.Add(offer);
}
}
Код использует Dispatcher
приложения для вызова вспомогательного лямбда-метода, который добавляет ваши элементы в коллекцию _currentOffers
в потоке пользовательского интерфейса. Вы можете сделать это синхронно или асинхронно , как указано в коде.
Я заменил ваш l oop запросом Linq, который приводит к перечислению на цель. Это дает преимущество, заключающееся в том, что вы ставите лямбду в очередь на выполнение в потоке пользовательского интерфейса один раз для всех элементов, а не бесчисленное количество раз для одного элемента, что более эффективно для уменьшения переключений контекста, которые могут резко снизить производительность. С другой стороны, добавление большого количества элементов может быть длительной операцией, которая блокирует поток пользовательского интерфейса и, следовательно, замораживает ваше приложение. Фактически, это зависит от фактической рабочей нагрузки и частоты добавления элементов, чтобы сделать правильный выбор для вашего варианта использования. Это также относится к самому механизму, будь то диспетчер или подход к операциям привязки.