-
Notifications
You must be signed in to change notification settings - Fork 1
/
ObservableConcurrentList.cs
291 lines (227 loc) · 11.7 KB
/
ObservableConcurrentList.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
namespace Nucs.Collections {
/// <summary>Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.</summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
[Serializable]
public class ObservableConcurrentList<T> : INotifyCollectionChanged, INotifyPropertyChanged, IList<T>, IReadOnlyList<T>, IList, IDisposable {
private readonly ConcurrentList<T> _list;
private SimpleMonitor _monitor = new SimpleMonitor();
public bool Remove(T item) {
var index = IndexOf(item);
if (index == -1)
return false;
RemoveAt(index);
return true;
}
public int Count => _list.Count;
/// <summary>Initializes a new instance of the <see cref="T:ObservableCollection`1" /> class.</summary>
public ObservableConcurrentList(ConcurrentList<T> list) {
_list = list;
}
/// <summary>Initializes a new instance of the <see cref="T:ObservableCollection`1" /> class.</summary>
public ObservableConcurrentList() {
_list = new ConcurrentList<T>();
}
/// <summary>Initializes a new instance of the <see cref="T:ObservableCollection`1" /> class that contains elements copied from the specified list.</summary>
/// <param name="list">The list from which the elements are copied.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="list" /> parameter cannot be <see langword="null" />.</exception>
public ObservableConcurrentList(List<T> list) {
if (list == null)
throw new ArgumentNullException(nameof(list));
_list = new ConcurrentList<T>(list);
}
/// <summary>Initializes a new instance of the <see cref="T:ObservableCollection`1" /> class that contains elements copied from the specified collection.</summary>
/// <param name="collection">The collection from which the elements are copied.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="collection" /> parameter cannot be <see langword="null" />.</exception>
public ObservableConcurrentList(IEnumerable<T> collection) {
if (collection == null)
throw new ArgumentNullException(nameof(collection));
_list = new ConcurrentList<T>(collection);
}
/// <summary>Moves the item at the specified index to a new location in the collection.</summary>
/// <param name="oldIndex">The zero-based index specifying the location of the item to be moved.</param>
/// <param name="newIndex">The zero-based index specifying the new location of the item.</param>
public void Move(int oldIndex, int newIndex) {
MoveItem(oldIndex, newIndex);
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged {
add => PropertyChanged += value;
remove => PropertyChanged -= value;
}
/// <summary>Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.</summary>
[field: NonSerialized]
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
int IList.Add(object value) {
return ((IList) _list).Add(value);
}
public bool Contains(object value) {
return ((IList) _list).Contains(value);
}
public T this[int index] {
get { return _list[index]; }
set {
CheckReentrancy();
T obj = this[index];
_list[index] = value;
OnPropertyChanged("Item[]");
OnCollectionChanged(NotifyCollectionChangedAction.Replace, (object) obj, (object) value, index);
}
}
public void Add(T item) {
_list.Add(item);
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, Count - 1);
}
/// <summary>Removes all items from the collection.</summary>
public void Clear() {
CheckReentrancy();
_list.Clear();
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionReset();
}
public bool Contains(T item) {
return _list.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex) {
_list.CopyTo(array, arrayIndex);
}
int IList.IndexOf(object value) {
return ((IList) _list).IndexOf(value);
}
void IList.Insert(int index, object value) {
Insert(index, (T) value);
}
void IList.Remove(object value) {
RemoveAt(((IList) this).IndexOf(value));
}
object IList.this[int index] {
get => ((IList) _list)[index];
set {
CheckReentrancy();
T obj = this[index];
((IList) _list)[index] = value;
OnPropertyChanged("Item[]");
OnCollectionChanged(NotifyCollectionChangedAction.Replace, (object) obj, (object) value, index);
}
}
public bool IsReadOnly => _list.IsReadOnly;
public bool IsFixedSize => ((IList) _list).IsFixedSize;
/// <summary>Removes the item at the specified index of the collection.</summary>
/// <param name="index">The zero-based index of the element to remove.</param>
public void RemoveAt(int index) {
CheckReentrancy();
T obj = this[index];
_list.RemoveAt(index);
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionChanged(NotifyCollectionChangedAction.Remove, (object) obj, index);
}
public int IndexOf(T item) {
return _list.IndexOf(item);
}
/// <summary>Inserts an item into the collection at the specified index.</summary>
/// <param name="index">The zero-based index at which <paramref name="item" /> should be inserted.</param>
/// <param name="item">The object to insert.</param>
public void Insert(int index, T item) {
CheckReentrancy();
_list.Insert(index, item);
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionChanged(NotifyCollectionChangedAction.Add, (object) item, index);
}
/// <summary>Moves the item at the specified index to a new location in the collection.</summary>
/// <param name="oldIndex">The zero-based index specifying the location of the item to be moved.</param>
/// <param name="newIndex">The zero-based index specifying the new location of the item.</param>
protected virtual void MoveItem(int oldIndex, int newIndex) {
CheckReentrancy();
T obj = this[oldIndex];
_list.RemoveAt(oldIndex);
_list.Insert(newIndex, obj);
OnPropertyChanged("Item[]");
OnCollectionChanged(NotifyCollectionChangedAction.Move, (object) obj, newIndex, oldIndex);
}
/// <summary>Raises the <see cref="E:ObservableCollection`1.PropertyChanged" /> event with the provided arguments.</summary>
/// <param name="e">Arguments of the event being raised.</param>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
if (PropertyChanged == null)
return;
PropertyChanged((object) this, e);
}
/// <summary>Occurs when a property value changes.</summary>
[field: NonSerialized]
protected virtual event PropertyChangedEventHandler PropertyChanged;
/// <summary>Raises the <see cref="E:ObservableCollection`1.CollectionChanged" /> event with the provided arguments.</summary>
/// <param name="e">Arguments of the event being raised.</param>
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) {
if (CollectionChanged == null)
return;
using (BlockReentrancy())
CollectionChanged((object) this, e);
}
/// <summary>Disallows reentrant attempts to change this collection.</summary>
/// <returns>An <see cref="T:System.IDisposable" /> object that can be used to dispose of the object.</returns>
public IDisposable BlockReentrancy() {
_monitor.Enter();
return (IDisposable) _monitor;
}
/// <summary>Checks for reentrant attempts to change this collection.</summary>
/// <exception cref="T:System.InvalidOperationException">If there was a call to <see cref="M:ObservableCollection`1.BlockReentrancy" /> of which the <see cref="T:System.IDisposable" /> return value has not yet been disposed of. Typically, this means when there are additional attempts to change this collection during a <see cref="E:ObservableCollection`1.CollectionChanged" /> event. However, it depends on when derived classes choose to call <see cref="M:ObservableCollection`1.BlockReentrancy" />.</exception>
protected void CheckReentrancy() {
if (_monitor.Busy && CollectionChanged != null && CollectionChanged.GetInvocationList().Length > 1)
throw new InvalidOperationException("ObservableCollectionReentrancyNotAllowed");
}
private void OnPropertyChanged(string propertyName) {
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index) {
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index, int oldIndex) {
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) {
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
}
private void OnCollectionReset() {
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
[Serializable]
private class SimpleMonitor : IDisposable {
private volatile int _busyCount;
public void Enter() {
Interlocked.Increment(ref _busyCount);
}
public void Dispose() {
Interlocked.Decrement(ref _busyCount);
}
public bool Busy => _busyCount > 0;
}
#region Implementation of IEnumerable
public IEnumerator<T> GetEnumerator() {
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return ((IEnumerable) _list).GetEnumerator();
}
#endregion
#region Implementation of IReadOnlyCollection<out T>
public void CopyTo(Array array, int index) {
((ICollection) _list).CopyTo(array, index);
}
public object SyncRoot => ((ICollection) _list).SyncRoot;
public bool IsSynchronized => ((ICollection) _list).IsSynchronized;
#endregion
#region Implementation of IDisposable
public void Dispose() {
_list.Dispose();
}
#endregion
}
}