DispatchingObservableList.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. using System;
  2. using System.Collections;
  3. #if !SILVERLIGHT
  4. using System.Collections.Concurrent;
  5. #endif
  6. using System.Collections.Generic;
  7. using System.Collections.Specialized;
  8. using System.ComponentModel;
  9. #if SILVERLIGHT
  10. using System.Windows;
  11. #endif
  12. using System.Windows.Threading;
  13. namespace MatrixIO.Collections
  14. {
  15. /// <summary>
  16. /// Observable List<typeparamref name="T"/> that automatically calls event handlers via the target object's Dispatcher if available.
  17. /// Targets that don't derive from DispatcherObject in WPF or DependencyObject in SilverLight are called on the same thread that
  18. /// the event is invoked from.
  19. ///
  20. /// Author: Jason S. Clary
  21. /// </summary>
  22. /// <typeparam name="T">The type of the elements</typeparam>
  23. public class DispatchingObservableList<T> : IList<T>, ICollection, INotifyCollectionChanged, INotifyPropertyChanged
  24. {
  25. public DispatchingObservableList()
  26. {
  27. _collection = new List<T>();
  28. }
  29. public DispatchingObservableList(int capacity)
  30. {
  31. _collection = new List<T>(capacity);
  32. }
  33. public DispatchingObservableList(IEnumerable<T> collection)
  34. {
  35. _collection = new List<T>(collection);
  36. }
  37. #region ICollection Implementation
  38. private readonly object _syncObject = new object();
  39. public void CopyTo(System.Array array, int index)
  40. {
  41. throw new System.NotImplementedException();
  42. }
  43. public bool IsSynchronized
  44. {
  45. get { return true; }
  46. }
  47. public object SyncRoot
  48. {
  49. get { return _syncObject; }
  50. }
  51. #endregion
  52. #region IList<T> Implementation
  53. private readonly List<T> _collection;
  54. public int IndexOf(T item)
  55. {
  56. lock (_syncObject) return _collection.IndexOf(item);
  57. }
  58. public void Insert(int index, T item)
  59. {
  60. lock (_syncObject)
  61. {
  62. _collection.Insert(index, item);
  63. OnNotifyPropertyChanged("Count");
  64. OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
  65. }
  66. }
  67. public void RemoveAt(int index)
  68. {
  69. lock(_syncObject)
  70. {
  71. var item = _collection[index];
  72. _collection.RemoveAt(index);
  73. OnNotifyPropertyChanged("Count");
  74. OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item, index);
  75. }
  76. }
  77. public T this[int index]
  78. {
  79. get
  80. {
  81. lock(_syncObject) return _collection[index];
  82. }
  83. set
  84. {
  85. lock (_syncObject)
  86. {
  87. // TODO: Handle possible insert scenario?
  88. var oldItem = _collection[index];
  89. _collection[index] = value;
  90. OnNotifyCollectionChanged(NotifyCollectionChangedAction.Replace, value, oldItem, index);
  91. }
  92. }
  93. }
  94. public void Add(T item)
  95. {
  96. lock(_syncObject)
  97. {
  98. _collection.Add(item);
  99. var index = _collection.IndexOf(item);
  100. OnNotifyPropertyChanged("Count");
  101. OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
  102. }
  103. }
  104. public void Clear()
  105. {
  106. lock(_syncObject)
  107. {
  108. _collection.Clear();
  109. OnNotifyPropertyChanged("Count");
  110. OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset);
  111. }
  112. }
  113. public bool Contains(T item)
  114. {
  115. lock(_syncObject) return _collection.Contains(item);
  116. }
  117. public void CopyTo(T[] array, int arrayIndex)
  118. {
  119. lock(_syncObject) _collection.CopyTo(array, arrayIndex);
  120. }
  121. public int Count
  122. {
  123. get { lock (_syncObject) return _collection.Count; }
  124. }
  125. public bool IsReadOnly
  126. {
  127. get { return ((IList<T>) _collection).IsReadOnly; }
  128. }
  129. public bool Remove(T item)
  130. {
  131. lock(_syncObject)
  132. {
  133. var index = _collection.IndexOf(item);
  134. if (_collection.Remove(item))
  135. {
  136. OnNotifyPropertyChanged("Count");
  137. OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item, index);
  138. return true;
  139. }
  140. return false;
  141. }
  142. }
  143. public IEnumerator<T> GetEnumerator()
  144. {
  145. lock(_syncObject)
  146. {
  147. foreach (var item in _collection)
  148. yield return item;
  149. }
  150. }
  151. IEnumerator IEnumerable.GetEnumerator()
  152. {
  153. return GetEnumerator();
  154. }
  155. #endregion
  156. #region Dispatching Event Handler Impelementation
  157. private readonly EventHandlerList _delegates = new EventHandlerList();
  158. #if SILVERLIGHT
  159. private readonly Dispatcher _dispatcher = Deployment.Current.Dispatcher;
  160. #else
  161. private readonly ConcurrentDictionary<Dispatcher, EventHandlerList> _dispatcherDelegates = new ConcurrentDictionary<Dispatcher, EventHandlerList>();
  162. #endif
  163. private void AddEventHandler(object eventObject, Delegate handler)
  164. {
  165. #if !SILVERLIGHT
  166. var target = handler.Target as DispatcherObject;
  167. if (target != null)
  168. {
  169. EventHandlerList eventHandlerList;
  170. if (!_dispatcherDelegates.TryGetValue(target.Dispatcher, out eventHandlerList))
  171. {
  172. eventHandlerList = new EventHandlerList();
  173. eventHandlerList.AddHandler(eventObject, handler);
  174. _dispatcherDelegates.AddOrUpdate(target.Dispatcher, eventHandlerList, (key, oldValue) =>
  175. {
  176. oldValue.AddHandlers(
  177. eventHandlerList);
  178. return oldValue;
  179. });
  180. }
  181. else eventHandlerList.AddHandler(eventObject, handler);
  182. }
  183. else
  184. #endif
  185. _delegates.AddHandler(eventObject, handler);
  186. }
  187. private void RemoveEventHandler(object eventObject, Delegate handler)
  188. {
  189. #if !SILVERLIGHT
  190. var target = handler.Target as DispatcherObject;
  191. if (target != null)
  192. {
  193. EventHandlerList eventHandlerList;
  194. if (!_dispatcherDelegates.TryGetValue(target.Dispatcher, out eventHandlerList))
  195. throw new ArgumentException();
  196. eventHandlerList.RemoveHandler(eventObject, handler);
  197. }
  198. else
  199. #endif
  200. _delegates.RemoveHandler(eventObject, handler);
  201. }
  202. #endregion
  203. #region INotifyPropertyChanged Implementation
  204. private readonly object _propertyChanged = new Object();
  205. public event PropertyChangedEventHandler PropertyChanged
  206. {
  207. add { AddEventHandler(_propertyChanged, value); }
  208. remove { RemoveEventHandler(_propertyChanged, value); }
  209. }
  210. void OnNotifyPropertyChanged(string propertyName)
  211. {
  212. OnNotifyPropertyChanged(new PropertyChangedEventArgs(propertyName));
  213. }
  214. void OnNotifyPropertyChanged(PropertyChangedEventArgs e)
  215. {
  216. var delegates = (PropertyChangedEventHandler)_delegates[_propertyChanged];
  217. if (delegates != null)
  218. #if SILVERLIGHT
  219. _dispatcher.BeginInvoke(() => delegates(this, e));
  220. #else
  221. delegates(this, e);
  222. foreach (var dispatcherDelegatePair in _dispatcherDelegates)
  223. {
  224. var eventHandler =
  225. (PropertyChangedEventHandler)dispatcherDelegatePair.Value[_propertyChanged];
  226. if (eventHandler == null) continue;
  227. if (dispatcherDelegatePair.Key.CheckAccess())
  228. eventHandler(this, e);
  229. else
  230. dispatcherDelegatePair.Key.BeginInvoke(DispatcherPriority.DataBind, new Action(() => eventHandler(this, e)));
  231. }
  232. #endif
  233. }
  234. #endregion
  235. #region INotifyCollectionChanged Implementation
  236. private readonly object _collectionChanged = new Object();
  237. public event NotifyCollectionChangedEventHandler CollectionChanged
  238. {
  239. add { AddEventHandler(_collectionChanged, value); }
  240. remove { RemoveEventHandler(_collectionChanged, value); }
  241. }
  242. void OnNotifyCollectionChanged(NotifyCollectionChangedAction action)
  243. {
  244. OnNotifyCollectionChanged(new NotifyCollectionChangedEventArgs(action));
  245. }
  246. void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, object changedItem, int index)
  247. {
  248. OnNotifyCollectionChanged(new NotifyCollectionChangedEventArgs(action, changedItem, index));
  249. }
  250. void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, object newItem, object oldItem, int index)
  251. {
  252. OnNotifyCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
  253. }
  254. void OnNotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
  255. {
  256. var delegates = (NotifyCollectionChangedEventHandler)_delegates[_collectionChanged];
  257. if (delegates != null)
  258. #if SILVERLIGHT
  259. _dispatcher.BeginInvoke(() => delegates(this, e));
  260. #else
  261. delegates(this, e);
  262. foreach (var dispatcherDelegatePair in _dispatcherDelegates)
  263. {
  264. var eventHandler =
  265. (NotifyCollectionChangedEventHandler)dispatcherDelegatePair.Value[_collectionChanged];
  266. if (eventHandler == null) continue;
  267. if (dispatcherDelegatePair.Key.CheckAccess())
  268. eventHandler(this, e);
  269. else
  270. dispatcherDelegatePair.Key.BeginInvoke(DispatcherPriority.DataBind, new Action(() => eventHandler(this, e)));
  271. }
  272. #endif
  273. }
  274. #endregion
  275. }
  276. #if SILVERLIGHT
  277. // SilverLight doesn't have an EventHandlerList collection
  278. public class EventHandlerList
  279. {
  280. private readonly Dictionary<object, Delegate> _dictionary = new Dictionary<object, Delegate>();
  281. public Delegate this[object key]
  282. {
  283. get
  284. {
  285. Delegate d;
  286. _dictionary.TryGetValue(key, out d);
  287. return d;
  288. }
  289. set { AddHandler(key, value); }
  290. }
  291. public void AddHandler(object key, Delegate value)
  292. {
  293. Delegate d;
  294. if (!_dictionary.TryGetValue(key, out d)) _dictionary.Add(key, value);
  295. else
  296. {
  297. Delegate newDelegate = Delegate.Combine(d, value);
  298. _dictionary[key] = newDelegate;
  299. }
  300. }
  301. public void RemoveHandler(object key, Delegate value)
  302. {
  303. Delegate d;
  304. if (_dictionary.TryGetValue(key, out d)) _dictionary[key] = Delegate.Remove(d, value);
  305. }
  306. }
  307. #endif
  308. }