2016-06-08 43 views
0

上自定义实现INotifyCollectionChanged的触发CollectionChanged事件,当我得到这个异常: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检查eventArgs中提供的索引是否属于集合;如果不是,则抛出主题中的异常。

在问题中报告的实现中,NotifyCollectionChangedEventHandler被延迟调用,此时可能已经更改了集合,并且eventArgs中提供的索引可能不再属于它。

避免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

相关问题