Reverse patches that should have gone to my fork rather than to head
[mono.git] / mcs / class / WindowsBase / System.Collections.ObjectModel / ObservableCollection.cs
index 54df9ec79cb7f5031bb1f7c4c7fdd2520f8cff41..1ea166cd9ac3134a1165f5513852e53a8112fb9d 100644 (file)
 // 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;
@@ -52,47 +96,103 @@ namespace System.Collections.ObjectModel {
 
                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