// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
+// Copyright 2011 Xamarin Inc.
//
// Authors:
// Chris Toshok (toshok@novell.com)
+// Brian O'Keefe (zer0keefie@gmail.com)
+// Marek Safar (marek.safar@gmail.com)
//
-using System;
+#if NET_4_0
+
+using System.Collections.ObjectModel;
+using System.Runtime.CompilerServices;
+[assembly:TypeForwardedTo (typeof (ObservableCollection<>))]
+
+#else
+
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
-namespace System.Collections.ObjectModel {
-
+namespace System.Collections.ObjectModel
+{
[Serializable]
- public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
- {
+ public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged {
+
+ private class Reentrant : IDisposable {
+ private int count = 0;
+
+ public Reentrant()
+ {
+ }
+
+ public void Enter()
+ {
+ count++;
+ }
+
+ public void Dispose()
+ {
+ count--;
+ }
+
+ public bool Busy
+ {
+ get { return count > 0; }
+ }
+ }
+
+ private Reentrant reentrant = new Reentrant ();
+
public ObservableCollection()
{
}
+ public ObservableCollection(IEnumerable<T> collection)
+ {
+ if (collection == null)
+ throw new ArgumentNullException ("collection");
+
+ foreach (var item in collection)
+ Add (item);
+ }
+
public ObservableCollection(List<T> list)
- : base (list)
- {
+ : base (list != null ? new List<T> (list) : null)
+ {
}
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
protected IDisposable BlockReentrancy ()
{
- return null;
+ reentrant.Enter ();
+ return reentrant;
}
protected void CheckReentrancy ()
{
+ NotifyCollectionChangedEventHandler eh = CollectionChanged;
+
+ // Only have a problem if we have more than one event listener.
+ if (reentrant.Busy && eh != null && eh.GetInvocationList ().Length > 1)
+ throw new InvalidOperationException ("Cannot modify the collection while reentrancy is blocked.");
}
protected override void ClearItems ()
{
+ CheckReentrancy ();
+
+ base.ClearItems ();
+
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Count"));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
}
protected override void InsertItem (int index, T item)
{
+ CheckReentrancy ();
+
+ base.InsertItem (index, item);
+
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, item, index));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Count"));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
}
public void Move (int oldIndex, int newIndex)
{
+ MoveItem (oldIndex, newIndex);
}
protected virtual void MoveItem (int oldIndex, int newIndex)
{
+ CheckReentrancy ();
+
+ T item = Items [oldIndex];
+ base.RemoveItem (oldIndex);
+ base.InsertItem (newIndex, item);
+
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Move, item, newIndex, oldIndex));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
}
protected virtual void OnCollectionChanged (NotifyCollectionChangedEventArgs e)
{
- if (CollectionChanged != null)
- CollectionChanged (this, e);
+ NotifyCollectionChangedEventHandler eh = CollectionChanged;
+
+ if (eh != null) {
+ // Make sure that the invocation is done before the collection changes,
+ // Otherwise there's a chance of data corruption.
+ using (BlockReentrancy ()) {
+ eh (this, e);
+ }
+ }
}
protected virtual void OnPropertyChanged (PropertyChangedEventArgs e)
{
- if (PropertyChanged != null)
- PropertyChanged (this, e);
+ PropertyChangedEventHandler eh = PropertyChanged;
+
+ if (eh != null)
+ eh (this, e);
}
protected override void RemoveItem (int index)
{
+ CheckReentrancy ();
+
+ T item = Items [index];
+
+ base.RemoveItem (index);
+
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, item, index));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Count"));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
}
protected override void SetItem (int index, T item)
{
+ CheckReentrancy ();
+
+ T oldItem = Items [index];
+
+ base.SetItem (index, item);
+
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace, item, oldItem, index));
+ OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
}
}
}
+#endif