Где я могу получить streamобезопасный CollectionView?

При обновлении коллекции бизнес-объектов в фоновом streamе я получаю это сообщение об ошибке:

Этот тип CollectionView не поддерживает изменения в SourceCollection из streamа, отличного от streamа Dispatcher.

Хорошо, это имеет смысл. Но также возникает вопрос: какая версия CollectionView поддерживает несколько streamов и как я могу использовать ее объекты?

Следующее – это улучшение реализации, которое было найдено Джонатаном. Во-первых, он запускает каждый обработчик событий связанного с ним диспетчера, а не предполагает, что все они находятся на одном диспетчере (UI). Во-вторых, он использует BeginInvoke для продолжения обработки, пока мы ожидаем, что диспетчер станет доступен. Это значительно ускоряет решение в ситуациях, когда фоновый stream выполняет множество обновлений с обработкой между ними. Возможно, что более важно, он преодолевает проблемы, вызванные блокировкой во время ожидания Invoke (взаимоблокировки могут возникать, например, при использовании WCF с ConcurrencyMode.Single).

public class MTObservableCollection : ObservableCollection { public override event NotifyCollectionChangedEventHandler CollectionChanged; protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged; if (CollectionChanged != null) foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList()) { DispatcherObject dispObj = nh.Target as DispatcherObject; if (dispObj != null) { Dispatcher dispatcher = dispObj.Dispatcher; if (dispatcher != null && !dispatcher.CheckAccess()) { dispatcher.BeginInvoke( (Action)(() => nh.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))), DispatcherPriority.DataBind); continue; } } nh.Invoke(this, e); } } } 

Поскольку мы используем BeginInvoke, возможно, уведомление об изменении отменяется до вызова обработчика. Это обычно приводит к тому, что «Индекс выходит за пределы допустимого диапазона». исключение вызывается, когда аргументы события проверяются на новое (измененное) состояние списка. Чтобы этого избежать, все замедленные события заменяются событиями сброса. В некоторых случаях это может привести к чрезмерной перерисовке.

Использование:

 System.Windows.Application.Current.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, (Action)delegate() { // Your Action Code }); 

Это сообщение от Bea Stollnitz объясняет это сообщение об ошибке и почему оно сформулировано так, как оно есть.

EDIT: Из блога Bea

К сожалению, этот код приводит к исключению: «NotSupportedException – этот тип CollectionView не поддерживает изменения в SourceCollection из streamа, отличного от streamа Dispatcher». Я понимаю, что это сообщение об ошибке заставляет людей думать, что если CollectionView они использование не поддерживает перекрестные изменения, то они должны найти тот, который делает. Ну, это сообщение об ошибке немного вводит в заблуждение: ни один из CollectionViews, который мы предоставляем из коробки, не поддерживает перекрестные streamи изменений коллекции. И нет, к сожалению, мы не можем исправить сообщение об ошибке на данный момент, мы очень сильно заблокированы.

Найден один.

 public class MTObservableCollection : ObservableCollection { public override event NotifyCollectionChangedEventHandler CollectionChanged; protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { var eh = CollectionChanged; if (eh != null) { Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList() let dpo = nh.Target as DispatcherObject where dpo != null select dpo.Dispatcher).FirstOrDefault(); if (dispatcher != null && dispatcher.CheckAccess() == false) { dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e))); } else { foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()) nh.Invoke(this, e); } } } } 

http://www.julmar.com/blog/mark/2009/04/01/AddingToAnObservableCollectionFromABackgroundThread.aspx

Вы также можете посмотреть: BindingOperations.EnableCollectionSynchronization .

См. Обновление до .NET 4.5: Элемент ItemsControl несовместим со своим источником элементов

Извините, не могу добавить комментарий, но все это неправильно.

ObservableCollection не является streamобезопасным. Не только из-за проблем диспетчера, но и вовсе не streamобезопасен (из msdn):

Любые публичные статические (Shared in Visual Basic) члены этого типа являются streamобезопасными. Любые члены экземпляра не гарантируют безопасность streamов.

Посмотрите здесь http://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx

Также возникает проблема при вызове BeginInvoke с действием «Сброс». «Сброс» – единственное действие, в котором обработчик должен смотреть на коллекцию. Если вы выполните BeginInvoke «Сброс», а затем сразу же запустите «Вставить» несколько действий «Добавить», чем обработчик, примет «Сброс» с уже обновленной коллекцией, а следующий «Добавить» создаст беспорядок.

Вот моя работа, которая работает. На самом деле я думаю об удалении BeginInvoke вообще:

Быстрая и надежная наблюдаемая коллекция

Если вы хотите периодически обновлять пользовательский интерфейс WPF и в то же время использовать интерфейс, вы можете использовать DispatcherTimer .

XAML

     

C #

  public partial class DownloadStats : Window { private MainWindow _parent; DispatcherTimer timer = new DispatcherTimer(); ObservableCollection fileViewList = new ObservableCollection(); public DownloadStats(MainWindow parent) { InitializeComponent(); _parent = parent; Owner = parent; timer.Interval = new TimeSpan(0, 0, 1); timer.Tick += new EventHandler(timer_Tick); timer.Start(); } void timer_Tick(object sender, EventArgs e) { dgDownloads.ItemsSource = null; fileViewList.Clear(); if (_parent.contentManagerWorkArea.Count > 0) { foreach (var item in _parent.contentManagerWorkArea) { FileView nf = item.Value.FileView; fileViewList.Add(nf); } } if (fileViewList.Count > 0) { lblFileCouner.Content = fileViewList.Count; dgDownloads.ItemsSource = fileViewList; } } } 

Ни один из них, просто используйте Dispatcher.BeginInvoke

Попробуй это:

 this.Dispatcher.Invoke(DispatcherPriority.Background, new Action( () => { //Code })); 

Вот версия VB, которую я сделал после некоторых поисковых запросов и небольших модов. Работает на меня.

  Imports System.Collections.ObjectModel Imports System.Collections.Specialized Imports System.ComponentModel Imports System.Reflection Imports System.Windows.Threading 'from: http://stackoverflow.com/questions/2137769/where-do-i-get-a-thread-safe-collectionview Public Class ThreadSafeObservableCollection(Of T) Inherits ObservableCollection(Of T) 'from: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx Protected Overrides Sub OnCollectionChanged(ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs) Dim doit As Boolean = False doit = (e.NewItems IsNot Nothing) AndAlso (e.NewItems.Count > 0) doit = doit OrElse ((e.OldItems IsNot Nothing) AndAlso (e.OldItems.Count > 0)) If (doit) Then Dim handler As NotifyCollectionChangedEventHandler = GetType(ObservableCollection(Of T)).GetField("CollectionChanged", BindingFlags.Instance Or BindingFlags.NonPublic).GetValue(Me) If (handler Is Nothing) Then Return End If For Each invocation As NotifyCollectionChangedEventHandler In handler.GetInvocationList Dim obj As DispatcherObject = invocation.Target If (obj IsNot Nothing) Then Dim disp As Dispatcher = obj.Dispatcher If (disp IsNot Nothing AndAlso Not (disp.CheckAccess())) Then disp.BeginInvoke( Sub() invocation.Invoke(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)) End Sub, DispatcherPriority.DataBind) Continue For End If End If invocation.Invoke(Me, e) Next End If End Sub End Class 

Небольшая ошибка в версии VB. Просто замените:

 Dim obj As DispatcherObject = invocation.Target 

От

 Dim obj As DispatcherObject = TryCast(invocation.Target, DispatcherObject) 
  • Как отобразить другое значение для выпадающего списка значений / выбранного элемента в WPF ComboBox?
  • Есть ли выбранная вкладка Измененное событие в стандартном WPF Tab Control
  • Как установить событие MouseOver / триггер для границы в XAML?
  • Изменение цвета и шрифта для некоторой части текста в WPF C #
  • Как сделать все элементы управления пропорционально пропорционально изменяемыми, когда окно максимизируется?
  • Введите ключевой обработчик события
  • Как связать перечисление с элементом управления combobox в WPF?
  • WPF Databinding: как мне получить доступ к «родительскому» контексту данных?
  • Установка свойства Style метки WPF в коде?
  • Индикатор выполнения с динамическим обновлением текста и текста
  • Предотвратить использование Dispatcher.Invoke в коде WPF.
  • Давайте будем гением компьютера.