BulkObservableCollection vs ObservableCollection

If you are working with huge number of data records to be shown on the UI control like datagrid, then you might have already come across some performance issues while trying to add (newly) fetched records into existing collection. If you are in this situation, then the solution for your issue could be BulkObservableCollection.

The main reason behind performance issue (UI lag), while inserting large sets, say 1000 rows, is that UI elements bound to this collection have subscribed to CollectionChanged event and as we add each record individually these events get triggered and does UI related updates accordingly. So instead of raising events (hence updating UI components) multiple number of times, wouldn’t it be nice to eat up all the notifications, update the collection and then raise just one event for updating the UI related components? That’s exactly what the concept behind the BulkObservableCollection is.

Though BulkObservableCollection is not readily available out of the box for Silverlight framework 4.0, one could easily extend the ObservableCollection to support AddRange and RemoveRange to support bulk inserts. While doing the add / remove range operations, we set a boolean flag, _isNotificationSuspended, and check for this flag before calling the base methods ‘OnCollectionChanged’ and ‘OnPropertyChanged’ as show below.

You could download the sample code in here and get a feel of what I am talking about by running this in your browser.

      #region Overrides of ObservableCollection<T>

      protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
      {
         if (!_isNotificationSuspended)
            base.OnCollectionChanged(e);
      }

      protected override void OnPropertyChanged(PropertyChangedEventArgs e)
      {
         if (!_isNotificationSuspended)
            base.OnPropertyChanged(e);
      }

      #endregion
	  
	  
      #region Implementation of IRangedObservableCollection<T>


      public void AddRange(IEnumerable<T> collection)
      {
         if (collection == null)
         {
            throw new ArgumentNullException("collection");
         }

         _isNotificationSuspended = true;
         int startIndex = this.Count;

         try
         {
            IList<T> items = base.Items;

            if (items != null)
            {
               using (IEnumerator<T> enumerator = collection.GetEnumerator())
               {
                  while (enumerator.MoveNext())
                  {
                     items.Add(enumerator.Current);
                  }
               }
            }
         }
         finally
         {
            _isNotificationSuspended = false;

            this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
         }
      }

      public void RemoveRange(IEnumerable<T> collection)
      {
         if (collection == null)
         {
            throw new ArgumentNullException("collection");
         }

         _isNotificationSuspended = true;
         int startIndex = this.Count;

         try
         {
            IList<T> items = base.Items;
            if (items != null)
            {
               using (IEnumerator<T> enumerator = collection.GetEnumerator())
               {
                  while (enumerator.MoveNext())
                  {
                     items.Remove(enumerator.Current);
                  }
               }
            }
         }
         finally
         {
            _isNotificationSuspended = false;

            this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
         }
      }

      public List<T> GetRange(int index, int count)
      {
         if (index < 0)
         {
            throw new ArgumentOutOfRangeException("index");
         }
         if (count < 0)
         {
            throw new ArgumentOutOfRangeException("index");
         }
         if ((this.Count - index) < count)
         {
            throw new ArgumentException("Invalid Offset Length");
         }

         return new List<T>(this.Items.Skip(index).Take(count));
      }

      #endregion

One thought on “BulkObservableCollection vs ObservableCollection

Leave a Reply

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