AttachableCollection`1.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.Collections.Specialized;
  4. using System.ComponentModel;
  5. using System.Globalization;
  6. using System.Windows;
  7. namespace HandyControl.Interactivity;
  8. public abstract class AttachableCollection<T> : FreezableCollection<T>, IAttachedObject
  9. where T : DependencyObject, IAttachedObject
  10. {
  11. private DependencyObject _associatedObject;
  12. private Collection<T> _snapshot;
  13. internal AttachableCollection()
  14. {
  15. INotifyCollectionChanged changed = this;
  16. changed.CollectionChanged += OnCollectionChanged;
  17. _snapshot = new Collection<T>();
  18. }
  19. public DependencyObject AssociatedObject
  20. {
  21. get
  22. {
  23. ReadPreamble();
  24. return _associatedObject;
  25. }
  26. }
  27. DependencyObject IAttachedObject.AssociatedObject => AssociatedObject;
  28. public void Attach(DependencyObject dependencyObject)
  29. {
  30. if (Equals(dependencyObject, AssociatedObject))
  31. return;
  32. if (AssociatedObject != null)
  33. throw new InvalidOperationException();
  34. if (Interaction.ShouldRunInDesignMode || !(bool) GetValue(DesignerProperties.IsInDesignModeProperty))
  35. {
  36. WritePreamble();
  37. _associatedObject = dependencyObject;
  38. WritePostscript();
  39. }
  40. OnAttached();
  41. }
  42. public void Detach()
  43. {
  44. OnDetaching();
  45. WritePreamble();
  46. _associatedObject = null;
  47. WritePostscript();
  48. }
  49. protected abstract void OnAttached();
  50. protected abstract void OnDetaching();
  51. internal abstract void ItemAdded(T item);
  52. internal abstract void ItemRemoved(T item);
  53. private void VerifyAdd(T item)
  54. {
  55. if (item == null) throw new ArgumentNullException(nameof(item));
  56. if (_snapshot.Contains(item))
  57. throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
  58. ExceptionStringTable.DuplicateItemInCollectionExceptionMessage, new object[]
  59. {
  60. typeof(T).Name,
  61. GetType().Name
  62. }));
  63. }
  64. private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  65. {
  66. switch (e.Action)
  67. {
  68. case NotifyCollectionChangedAction.Add:
  69. var enumerator1 = e.NewItems.GetEnumerator();
  70. try
  71. {
  72. while (enumerator1.MoveNext())
  73. {
  74. var current = (T) enumerator1.Current;
  75. try
  76. {
  77. VerifyAdd(current);
  78. ItemAdded(current);
  79. }
  80. finally
  81. {
  82. _snapshot.Insert(IndexOf(current), current);
  83. }
  84. }
  85. break;
  86. }
  87. finally
  88. {
  89. if (enumerator1 is IDisposable disposable)
  90. disposable.Dispose();
  91. }
  92. case NotifyCollectionChangedAction.Remove:
  93. var enumerator2 = e.OldItems.GetEnumerator();
  94. try
  95. {
  96. while (enumerator2.MoveNext())
  97. {
  98. var current = (T) enumerator2.Current;
  99. ItemRemoved(current);
  100. _snapshot.Remove(current);
  101. }
  102. break;
  103. }
  104. finally
  105. {
  106. if (enumerator2 is IDisposable disposable)
  107. disposable.Dispose();
  108. }
  109. case NotifyCollectionChangedAction.Replace:
  110. foreach (T oldItem in e.OldItems)
  111. {
  112. ItemRemoved(oldItem);
  113. _snapshot.Remove(oldItem);
  114. }
  115. var enumerator3 = e.NewItems.GetEnumerator();
  116. try
  117. {
  118. while (enumerator3.MoveNext())
  119. {
  120. var current = (T) enumerator3.Current;
  121. try
  122. {
  123. VerifyAdd(current);
  124. ItemAdded(current);
  125. }
  126. finally
  127. {
  128. _snapshot.Insert(IndexOf(current), current);
  129. }
  130. }
  131. break;
  132. }
  133. finally
  134. {
  135. if (enumerator3 is IDisposable disposable)
  136. disposable.Dispose();
  137. }
  138. case NotifyCollectionChangedAction.Reset:
  139. foreach (var obj in _snapshot)
  140. ItemRemoved(obj);
  141. _snapshot = new Collection<T>();
  142. using (var enumerator4 = GetEnumerator())
  143. {
  144. while (enumerator4.MoveNext())
  145. {
  146. var current = enumerator4.Current;
  147. VerifyAdd(current);
  148. ItemAdded(current);
  149. }
  150. break;
  151. }
  152. }
  153. }
  154. }