|
16 | 16 | using System.Reactive.Subjects;
|
17 | 17 | using System.Threading;
|
18 | 18 | using System.Linq;
|
| 19 | +using System.Collections.Specialized; |
19 | 20 |
|
20 | 21 | namespace GitHub.Collections
|
21 | 22 | {
|
@@ -47,53 +48,107 @@ public static ObservableCollection<T> CreateListenerCollection<T>(this ITracking
|
47 | 48 | IList<T> stickieItemsOnTop = null)
|
48 | 49 | where T : class, ICopyable<T>
|
49 | 50 | {
|
50 |
| - var col = new ObservableCollection<T>(stickieItemsOnTop ?? Enumerable.Empty<T>()); |
51 |
| - tcol.CollectionChanged += (s, e) => |
| 51 | + if (stickieItemsOnTop == null) |
52 | 52 | {
|
53 |
| - var offset = 0; |
54 |
| - if (stickieItemsOnTop != null) |
55 |
| - { |
56 |
| - foreach (var item in stickieItemsOnTop) |
57 |
| - { |
58 |
| - if (col.Contains(item)) |
59 |
| - offset++; |
60 |
| - } |
61 |
| - } |
| 53 | + stickieItemsOnTop = new T[0]; |
| 54 | + } |
| 55 | + |
| 56 | + var col = new ObservableCollection<T>(stickieItemsOnTop.Concat(tcol)); |
| 57 | + tcol.CollectionChanged += (_, e) => UpdateStickieItems(col, e, stickieItemsOnTop); |
| 58 | + return col; |
| 59 | + } |
| 60 | + |
| 61 | + /// <summary> |
| 62 | + /// Creates an observable collection that tracks an <see cref="ITrackingCollection{T}"/> |
| 63 | + /// and adds a sticky item to the top of the collection when a related selection is null. |
| 64 | + /// </summary> |
| 65 | + /// <typeparam name="T">The type of items in the collection.</typeparam> |
| 66 | + /// <param name="tcol">The source tracking collection</param> |
| 67 | + /// <param name="stickieItemOnTop">The sticky item to add to the top of the collection.</param> |
| 68 | + /// <param name="selection"> |
| 69 | + /// The current selection. If null or equal to the sticky item then the sticky item will be |
| 70 | + /// added to the collection. |
| 71 | + /// </param> |
| 72 | + /// <returns>An <see cref="ObservableCollection{T}"/>.</returns> |
| 73 | + public static ObservableCollection<T> CreateListenerCollection<T>(this ITrackingCollection<T> tcol, |
| 74 | + T stickieItemOnTop, |
| 75 | + IObservable<T> selection) |
| 76 | + where T : class, ICopyable<T> |
| 77 | + { |
| 78 | + Debug.Assert(stickieItemOnTop != null, "stickieItemOnTop may not be null in CreateListenerCollection"); |
| 79 | + Debug.Assert(selection != null, "selection may not be null in CreateListenerCollection"); |
| 80 | + |
| 81 | + var stickieItems = new[] { stickieItemOnTop }; |
| 82 | + var result = new ObservableCollection<T>(tcol); |
| 83 | + var hasSelection = false; |
| 84 | + |
| 85 | + tcol.CollectionChanged += (_, e) => |
| 86 | + { |
| 87 | + UpdateStickieItems(result, e, hasSelection ? stickieItems : null); |
| 88 | + }; |
62 | 89 |
|
63 |
| - if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move) |
| 90 | + selection.Subscribe(x => |
| 91 | + { |
| 92 | + hasSelection = x != null && !object.Equals(x, stickieItemOnTop); |
| 93 | + var hasStickie = result.FirstOrDefault() == stickieItemOnTop; |
| 94 | + |
| 95 | + if (hasSelection && !hasStickie) |
64 | 96 | {
|
65 |
| - for (int i = 0, oldIdx = e.OldStartingIndex, newIdx = e.NewStartingIndex; |
66 |
| - i < e.OldItems.Count; i++, oldIdx++, newIdx++) |
67 |
| - { |
68 |
| - col.Move(oldIdx + offset, newIdx + offset); |
69 |
| - } |
| 97 | + result.Insert(0, stickieItemOnTop); |
70 | 98 | }
|
71 |
| - else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) |
| 99 | + else if (hasStickie) |
72 | 100 | {
|
73 |
| - foreach (T item in e.NewItems) |
74 |
| - col.Add(item); |
| 101 | + result.Remove(stickieItemOnTop); |
75 | 102 | }
|
76 |
| - else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) |
77 |
| - { |
78 |
| - foreach (T item in e.OldItems) |
79 |
| - col.Remove(item); |
80 |
| - } |
81 |
| - else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace) |
| 103 | + }); |
| 104 | + |
| 105 | + return result; |
| 106 | + } |
| 107 | + |
| 108 | + static void UpdateStickieItems<T>( |
| 109 | + ObservableCollection<T> col, |
| 110 | + NotifyCollectionChangedEventArgs e, |
| 111 | + IList<T> stickieItemsOnTop) |
| 112 | + { |
| 113 | + var offset = 0; |
| 114 | + if (stickieItemsOnTop != null) |
| 115 | + { |
| 116 | + if (object.Equals(col.FirstOrDefault(), stickieItemsOnTop.FirstOrDefault())) |
| 117 | + offset = stickieItemsOnTop.Count; |
| 118 | + } |
| 119 | + |
| 120 | + if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move) |
| 121 | + { |
| 122 | + for (int i = 0, oldIdx = e.OldStartingIndex, newIdx = e.NewStartingIndex; |
| 123 | + i < e.OldItems.Count; i++, oldIdx++, newIdx++) |
82 | 124 | {
|
83 |
| - for (int i = 0, idx = e.OldStartingIndex; i < e.OldItems.Count; i++, idx++) |
84 |
| - col[idx + offset] = (T)e.NewItems[i]; |
| 125 | + col.Move(oldIdx + offset, newIdx + offset); |
85 | 126 | }
|
86 |
| - else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset) |
| 127 | + } |
| 128 | + else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) |
| 129 | + { |
| 130 | + foreach (T item in e.NewItems) |
| 131 | + col.Add(item); |
| 132 | + } |
| 133 | + else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) |
| 134 | + { |
| 135 | + foreach (T item in e.OldItems) |
| 136 | + col.Remove(item); |
| 137 | + } |
| 138 | + else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace) |
| 139 | + { |
| 140 | + for (int i = 0, idx = e.OldStartingIndex; i < e.OldItems.Count; i++, idx++) |
| 141 | + col[idx + offset] = (T)e.NewItems[i]; |
| 142 | + } |
| 143 | + else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset) |
| 144 | + { |
| 145 | + col.Clear(); |
| 146 | + if (stickieItemsOnTop != null) |
87 | 147 | {
|
88 |
| - col.Clear(); |
89 |
| - if (stickieItemsOnTop != null) |
90 |
| - { |
91 |
| - foreach (var item in stickieItemsOnTop) |
92 |
| - col.Add(item); |
93 |
| - } |
| 148 | + foreach (var item in stickieItemsOnTop) |
| 149 | + col.Add(item); |
94 | 150 | }
|
95 |
| - }; |
96 |
| - return col; |
| 151 | + } |
97 | 152 | }
|
98 | 153 | }
|
99 | 154 |
|
|
0 commit comments