2016-06-08 68 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