2016-06-08 5 views
0

ich diese Ausnahme erhalten, wenn ein Ereignisses Collection auf einer kundenspezifischen Implementierung von INotifyCollectionChanged Auslösung:System.InvalidOperationException ‚n‘ Index in der Ereigniserfassung Änderung ist für die Sammlung der Größe nicht gültig ‚0‘

Eine Ausnahme des Typs ‚System.InvalidOperationException‘ aufgetreten in PresentationFramework.dll wurde aber in Benutzercode

Zusätzliche Informationen nicht behandelt: ‚25‘ Index in Sammlung Änderungsereignis gültig für die Sammlung der Größe ‚0‘ ist es nicht.

Ein XAML-Datagrid ist als ItemsSource an die Auflistung gebunden.

Wie kann diese Ausnahme vermieden werden?

Der Code folgt:

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); 
          } 
        } 
       } 
      }   
    } 

Der Fehler in der folgenden Zeile auftritt:

nh.Invoke(this, e); 

Dank!

Antwort

0

Der Punkt ist, dass (von Design) nh.Invoke (this, e); wird asynchron aufgerufen. Wenn die Auflistung in einem XAML gebunden ist und die Auflistung geändert wird, wird die private Methode AdjustBefore von System.Windows.Data.ListCollectionView aufgerufen. Hier überprüft ListCollectionView, dass die im eventArgs bereitgestellten Indizes zur Auflistung gehören. Wenn nicht, wird die Ausnahme im Betreff ausgelöst.

In der in der Frage beschriebenen Implementierung wird der NotifyCollectionChangedEventHandler zu einem verzögerten Zeitpunkt aufgerufen, wenn die Auflistung möglicherweise bereits geändert wurde, und die im eventArgs bereitgestellten Indizes gehören möglicherweise nicht mehr dazu.

Eine Möglichkeit zu vermeiden, dass die ListCollectionView diese Überprüfung durchführt, ist das Ersetzen der Ereignisargumente durch ein neues Ereignisargument, das anstatt die hinzugefügten oder entfernten Elemente zu melden, nur eine Zurücksetzungsaktion hat (natürlich ist die Effizienz verloren!).

Hier ist eine funktionierende Implementierung:

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); 
       } 
      } 
     } 
    } 
} 

Referenzen: https://msdn.microsoft.com/library/system.windows.data.listcollectionview(v=vs.110).aspx

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

Verwandte Themen