2016-06-08 1 views
0

Я получаю это исключение, когда запуская событие CollectionChanged на пользовательскую реализации INotifyCollectionChanged:System.InvalidOperationException «п» индекс в случае изменения коллекции не является действительным для сбора размера «0»

Исключения из «System.InvalidOperationException» типа произошло в PresentationFramework.dll, но не был обработан в пользовательском коде

Дополнительная информация: индекс «25» в случае изменения коллекция не действительна для сбора размера «0».

XAML Datagrid привязан к коллекции как ItemsSource.

Как избежать этого исключения?

Код следующим образом:

public class MultiThreadObservableCollection<T> : ObservableCollection<T> 
{ 
    private readonly object lockObject; 

    public MultiThreadObservableCollection() 
    { 
     lockObject = new object(); 
    } 

    private NotifyCollectionChangedEventHandler myPropertyChangedDelegate; 


    public override event NotifyCollectionChangedEventHandler CollectionChanged 
    { 
     add 
     { 
      lock (this.lockObject) 
      { 
       myPropertyChangedDelegate += value; 
      } 
     } 
     remove 
     { 
      lock (this.lockObject) 
      { 
       myPropertyChangedDelegate -= value; 
      } 
     } 
    } 

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
      var eh = this.myPropertyChangedDelegate; 
      if (eh != null) 
      { 
       Dispatcher dispatcher; 
       lock (this.lockObject) 
       { 
        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)(() => this.OnCollectionChanged(e))); 
       } 
       else 
       { 
        lock (this.lockObject) 
        { 
          foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()) 
          { 
           nh.Invoke(this, e); 
          } 
        } 
       } 
      }   
    } 

Ошибка возникает в следующей строке:

nh.Invoke(this, e); 

Спасибо!

ответ

0

Дело в том, что (по дизайну) nh.Invoke (this, e); называется асинхронно. Когда коллекция привязана, в XAML и изменения коллекции, вызывается собственный метод System.Windows.Data.ListCollectionView AdjustBefore. Здесь ListCollectionView проверяет, что индексы, предоставленные в eventArg, принадлежат коллекции; если нет, исключается исключение в теме.

В реализации, указанной в вопросе, NotifyCollectionChangedEventHandler вызывается с задержкой, когда коллекция может быть изменена уже, а индексы, предоставленные в eventArg, могут не принадлежать ей.

Способ избежать, чтобы ListCollectionView выполнял эту проверку, чтобы заменить eventargs новым eventargs, который вместо сообщения добавленных или удаленных элементов просто имеет действие Reset (конечно, эффективность теряется!).

Вот рабочая реализация:

public class MultiThreadObservableCollection<T> : ObservableCollectionEnh<T> 
{ 
    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)(() => this.OnCollectionChanged(e))); 
      } 
      else 
      { 
       // IMPORTANT NOTE: 
       // We send a Reset eventargs (this is inefficient). 
       // If we send the event with the original eventargs, it could contain indexes that do not belong to the collection any more, 
       // causing an InvalidOperationException in the with message like: 
       // 'n2' index in collection change event is not valid for collection of size 'n2'. 
       NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); 

       foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()) 
       { 
        nh.Invoke(this, notifyCollectionChangedEventArgs); 
       } 
      } 
     } 
    } 
} 

Ссылки: https://msdn.microsoft.com/library/system.windows.data.listcollectionview(v=vs.110).aspx

https://msdn.microsoft.com/library/ms752284(v=vs.110).aspx

Смежные вопросы