System.InvalidOperationException – Cannot change ObservableCollection during a CollectionChanged event

Loading

The System.InvalidOperationException with the message “Cannot change ObservableCollection during a CollectionChanged event” occurs in C# when you attempt to modify an ObservableCollection<T> while it is in the process of raising its CollectionChanged event. This typically happens when a handler for the CollectionChanged event modifies the collection, causing a re-entrant modification.

Common Causes and Solutions

  1. Modifying Collection in CollectionChanged Handler:
    If you modify the collection (e.g., add, remove, or clear items) inside a CollectionChanged event handler, it will trigger the exception.
   var collection = new ObservableCollection<int>();
   collection.CollectionChanged += (sender, e) =>
   {
       if (e.Action == NotifyCollectionChangedAction.Add)
       {
           collection.Add(42); // InvalidOperationException – Cannot modify collection during CollectionChanged event
       }
   };

   collection.Add(10); // Triggers the exception

Fix: Avoid modifying the collection directly in the CollectionChanged handler. Use a queue or defer the modification.

   var collection = new ObservableCollection<int>();
   var pendingChanges = new Queue<int>();

   collection.CollectionChanged += (sender, e) =>
   {
       if (e.Action == NotifyCollectionChangedAction.Add)
       {
           pendingChanges.Enqueue(42); // Queue the change instead of applying it directly
       }
   };

   collection.Add(10);

   // Apply pending changes after the event is complete
   while (pendingChanges.Count > 0)
   {
       collection.Add(pendingChanges.Dequeue());
   }
  1. Nested Modifications:
    If multiple CollectionChanged handlers are registered and one handler modifies the collection while another is still processing, this exception can occur. Fix: Ensure that modifications are deferred or handled in a way that avoids re-entrant changes.
   var collection = new ObservableCollection<int>();
   var isProcessing = false;

   collection.CollectionChanged += (sender, e) =>
   {
       if (isProcessing) return; // Avoid re-entrant modifications
       isProcessing = true;

       if (e.Action == NotifyCollectionChangedAction.Add)
       {
           // Perform modifications safely
           collection.Add(42);
       }

       isProcessing = false;
   };

   collection.Add(10);
  1. Using ObservableCollection with Data Binding:
    In WPF or other UI frameworks, data binding can trigger CollectionChanged events. Modifying the collection in response to these events can cause this exception. Fix: Use Dispatcher to defer modifications until the event is complete.
   var collection = new ObservableCollection<int>();

   collection.CollectionChanged += (sender, e) =>
   {
       if (e.Action == NotifyCollectionChangedAction.Add)
       {
           Application.Current.Dispatcher.BeginInvoke(new Action(() =>
           {
               collection.Add(42); // Defer the modification
           }));
       }
   };

   collection.Add(10);
  1. Batch Updates:
    If you need to make multiple changes to the collection, modifying it repeatedly during CollectionChanged events can trigger this exception. Fix: Use a temporary list or disable notifications temporarily.
   var collection = new ObservableCollection<int>();
   var tempList = new List<int>();

   collection.CollectionChanged += (sender, e) =>
   {
       if (e.Action == NotifyCollectionChangedAction.Add)
       {
           tempList.Add(42); // Store changes in a temporary list
       }
   };

   collection.Add(10);

   // Apply changes after the event is complete
   foreach (var item in tempList)
   {
       collection.Add(item);
   }
  1. Custom Collections:
    If you are using a custom collection that inherits from ObservableCollection<T>, ensure that modifications are handled safely. Fix: Override methods like InsertItem or RemoveItem to handle modifications safely.
   public class SafeObservableCollection<T> : ObservableCollection<T>
   {
       protected override void InsertItem(int index, T item)
       {
           // Perform custom logic safely
           base.InsertItem(index, item);
       }
   }

Leave a Reply

Your email address will not be published. Required fields are marked *