33af6dae735101cb2026d853bfb4790b1ab9be32
[mono.git] / mcs / class / System / System.Collections.ObjectModel / ObservableCollection.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
21 // Copyright 2011 Xamarin Inc.
22 //
23 // Authors:
24 //      Chris Toshok (toshok@novell.com)
25 //      Brian O'Keefe (zer0keefie@gmail.com)
26 //      Marek Safar (marek.safar@gmail.com)
27 //
28
29 #if NET_4_0 || MOBILE
30 using System.Collections.Generic;
31 using System.Collections.Specialized;
32 using System.ComponentModel;
33 using System.Runtime.CompilerServices;
34
35 namespace System.Collections.ObjectModel
36 {
37         [Serializable]
38 #if !MOBILE
39         [TypeForwardedFrom (Consts.WindowsBase_3_0)]
40 #endif
41         public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged {
42                 [Serializable]
43                 sealed class Reentrant : IDisposable {
44                         private int count = 0;
45
46                         public Reentrant()
47                         {
48                         }
49
50                         public void Enter()
51                         {
52                                 count++;
53                         }
54
55                         public void Dispose()
56                         {
57                                 count--;
58                         }
59
60                         public bool Busy
61                         {
62                                 get { return count > 0; }
63                         }
64                 }
65
66                 private Reentrant reentrant = new Reentrant ();
67
68                 public ObservableCollection ()
69                 {
70                 }
71
72                 public ObservableCollection (IEnumerable<T> collection)
73                 {
74                         if (collection == null)
75                                 throw new ArgumentNullException ("collection");
76
77                         foreach (var item in collection)
78                                 Add (item);
79                 }
80
81                 public ObservableCollection (List<T> list)
82                         : base (list != null ? new List<T> (list) : null)
83                 {
84                 }
85
86                 public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
87                 protected virtual event PropertyChangedEventHandler PropertyChanged;
88
89                 event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged {
90                         add { this.PropertyChanged += value; }
91                         remove { this.PropertyChanged -= value; }
92                 }
93
94                 protected IDisposable BlockReentrancy ()
95                 {
96                         reentrant.Enter ();
97                         return reentrant;
98                 }
99
100                 protected void CheckReentrancy ()
101                 {
102                         NotifyCollectionChangedEventHandler eh = CollectionChanged;
103
104                         // Only have a problem if we have more than one event listener.
105                         if (reentrant.Busy && eh != null && eh.GetInvocationList ().Length > 1)
106                                 throw new InvalidOperationException ("Cannot modify the collection while reentrancy is blocked.");
107                 }
108
109                 protected override void ClearItems ()
110                 {
111                         CheckReentrancy ();
112
113                         base.ClearItems ();
114
115                         OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset));
116                         OnPropertyChanged (new PropertyChangedEventArgs ("Count"));
117                         OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
118                 }
119
120                 protected override void InsertItem (int index, T item)
121                 {
122                         CheckReentrancy ();
123
124                         base.InsertItem (index, item);
125
126                         OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, item, index));
127                         OnPropertyChanged (new PropertyChangedEventArgs ("Count"));
128                         OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
129                 }
130
131                 public void Move (int oldIndex, int newIndex)
132                 {
133                         MoveItem (oldIndex, newIndex);
134                 }
135
136                 protected virtual void MoveItem (int oldIndex, int newIndex)
137                 {
138                         CheckReentrancy ();
139
140                         T item = Items [oldIndex];
141                         base.RemoveItem (oldIndex);
142                         base.InsertItem (newIndex, item);
143
144                         OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Move, item, newIndex, oldIndex));
145                         OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
146                 }
147
148                 protected virtual void OnCollectionChanged (NotifyCollectionChangedEventArgs e)
149                 {
150                         NotifyCollectionChangedEventHandler eh = CollectionChanged;
151
152                         if (eh != null) {
153                                 // Make sure that the invocation is done before the collection changes,
154                                 // Otherwise there's a chance of data corruption.
155                                 using (BlockReentrancy ()) {
156                                         eh (this, e);
157                                 }
158                         }
159                 }
160
161                 protected virtual void OnPropertyChanged (PropertyChangedEventArgs e)
162                 {
163                         PropertyChangedEventHandler eh = PropertyChanged;
164
165                         if (eh != null)
166                                 eh (this, e);
167                 }
168
169                 protected override void RemoveItem (int index)
170                 {
171                         CheckReentrancy ();
172
173                         T item = Items [index];
174
175                         base.RemoveItem (index);
176
177                         OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, item, index));
178                         OnPropertyChanged (new PropertyChangedEventArgs ("Count"));
179                         OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
180                 }
181
182                 protected override void SetItem (int index, T item)
183                 {
184                         CheckReentrancy ();
185
186                         T oldItem = Items [index];
187
188                         base.SetItem (index, item);
189
190                         OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace, item, oldItem, index));
191                         OnPropertyChanged (new PropertyChangedEventArgs ("Item[]"));
192                 }
193         }
194 }
195 #endif