Skip to content

Commit 9e84168

Browse files
committed
DerivedCollection onRemove clean up
1 parent c5f7e5d commit 9e84168

File tree

3 files changed

+163
-39
lines changed

3 files changed

+163
-39
lines changed

ReactiveUI.Tests/ReactiveCollectionTest.cs

+2-5
Original file line numberDiff line numberDiff line change
@@ -967,8 +967,7 @@ public void DerviedCollectionShouldHandleItemsRemoved()
967967
var fixture = new ReactiveList<TestFixture>(
968968
input.Select(x => new TestFixture() { IsOnlyOneWord = x }));
969969

970-
var output = fixture.CreateDerivedCollection(new Func<TestFixture, IDisposable>(x => Disposable.Create(() => disposed.Add(x))),
971-
onRemoved: item => item.Dispose());
970+
var output = fixture.CreateDerivedCollection(x => Disposable.Create(() => disposed.Add(x)), item => item.Dispose());
972971

973972
fixture.Add(new TestFixture() { IsOnlyOneWord = "Hello" });
974973
Assert.Equal(5, output.Count);
@@ -1697,8 +1696,7 @@ ReactiveList<TextModel> makeAsyncCollection(int maxSize)
16971696
}
16981697

16991698
[Fact]
1700-
public void TestDelayNotifications()
1701-
{
1699+
public void TestDelayNotifications() {
17021700
var maxSize = 10;
17031701
var data = makeAsyncCollection(maxSize);
17041702

@@ -1708,7 +1706,6 @@ public void TestDelayNotifications()
17081706

17091707
var derivedList = list.CreateDerivedCollection(
17101708
m => m.Value, m => m.HasData, (a, b) => a.Text.CompareTo(b.Text),
1711-
null,
17121709
Observable.Never(4) /*list.ShouldReset*/,
17131710
scheduler: RxApp.MainThreadScheduler);
17141711

ReactiveUI.Winforms/Winforms/ReactiveDerivedBindingListMixins.cs

+70-3
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ public static class ObservableCollectionMixin
105105
/// </summary>
106106
/// <param name="selector">A Select function that will be run on each
107107
/// item.</param>
108+
/// <param name="onRemoved">An action that is called on each item when
109+
/// it is removed.</param>
108110
/// <param name="filter">A filter to determine whether to exclude items
109111
/// in the derived collection.</param>
110112
/// <param name="orderer">A comparator method to determine the ordering of
@@ -118,9 +120,9 @@ public static class ObservableCollectionMixin
118120
public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew, TDontCare>(
119121
this IEnumerable<T> This,
120122
Func<T, TNew> selector,
123+
Action<TNew> removed,
121124
Func<T, bool> filter = null,
122125
Func<TNew, TNew, int> orderer = null,
123-
Action<TNew> removed = null,
124126
IObservable<TDontCare> signalReset = null)
125127
{
126128
Contract.Requires(selector != null);
@@ -134,6 +136,72 @@ public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew
134136
return new ReactiveDerivedBindingList<T, TNew>(This, selector, filter, orderer, removed, reset);
135137
}
136138

139+
/// <summary>
140+
/// Creates a collection whose contents will "follow" another
141+
/// collection; this method is useful for creating ViewModel collections
142+
/// that are automatically updated when the respective Model collection
143+
/// is updated.
144+
///
145+
/// Note that even though this method attaches itself to any
146+
/// IEnumerable, it will only detect changes from objects implementing
147+
/// INotifyCollectionChanged (like ReactiveList). If your source
148+
/// collection doesn't implement this, signalReset is the way to signal
149+
/// the derived collection to reorder/refilter itself.
150+
/// </summary>
151+
/// <param name="selector">A Select function that will be run on each
152+
/// item.</param>
153+
/// <param name="filter">A filter to determine whether to exclude items
154+
/// in the derived collection.</param>
155+
/// <param name="orderer">A comparator method to determine the ordering of
156+
/// the resulting collection.</param>
157+
/// <param name="signalReset">When this Observable is signalled,
158+
/// the derived collection will be manually
159+
/// reordered/refiltered.</param>
160+
/// <returns>A new collection whose items are equivalent to
161+
/// Collection.Select().Where().OrderBy() and will mirror changes
162+
/// in the initial collection.</returns>
163+
public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew, TDontCare>(
164+
this IEnumerable<T> This,
165+
Func<T, TNew> selector,
166+
Func<T, bool> filter = null,
167+
Func<TNew, TNew, int> orderer = null,
168+
IObservable<TDontCare> signalReset = null)
169+
{
170+
return This.CreateDerivedBindingList(selector, null, filter, orderer, signalReset);
171+
}
172+
173+
/// <summary>
174+
/// Creates a collection whose contents will "follow" another
175+
/// collection; this method is useful for creating ViewModel collections
176+
/// that are automatically updated when the respective Model collection
177+
/// is updated.
178+
///
179+
/// Be aware that this overload will result in a collection that *only*
180+
/// updates if the source implements INotifyCollectionChanged. If your
181+
/// list changes but isn't a ReactiveList/ObservableCollection,
182+
/// you probably want to use the other overload.
183+
/// </summary>
184+
/// <param name="selector">A Select function that will be run on each
185+
/// item.</param>
186+
/// /// <param name="onRemoved">An action that is called on each item when
187+
/// it is removed.</param>
188+
/// <param name="filter">A filter to determine whether to exclude items
189+
/// in the derived collection.</param>
190+
/// <param name="orderer">A comparator method to determine the ordering of
191+
/// the resulting collection.</param>
192+
/// <returns>A new collection whose items are equivalent to
193+
/// Collection.Select().Where().OrderBy() and will mirror changes
194+
/// in the initial collection.</returns>
195+
public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew>(
196+
this IEnumerable<T> This,
197+
Func<T, TNew> selector,
198+
Action<TNew> removed,
199+
Func<T, bool> filter = null,
200+
Func<TNew, TNew, int> orderer = null)
201+
{
202+
return This.CreateDerivedBindingList(selector, removed, filter, orderer, (IObservable<Unit>)null);
203+
}
204+
137205
/// <summary>
138206
/// Creates a collection whose contents will "follow" another
139207
/// collection; this method is useful for creating ViewModel collections
@@ -158,10 +226,9 @@ public static IReactiveDerivedBindingList<TNew> CreateDerivedBindingList<T, TNew
158226
this IEnumerable<T> This,
159227
Func<T, TNew> selector,
160228
Func<T, bool> filter = null,
161-
Action<TNew> removed = null,
162229
Func<TNew, TNew, int> orderer = null)
163230
{
164-
return This.CreateDerivedBindingList(selector, filter, orderer, removed, (IObservable<Unit>)null);
231+
return This.CreateDerivedBindingList(selector, null, filter, orderer, (IObservable<Unit>)null);
165232
}
166233
}
167234
}

ReactiveUI/ReactiveCollectionMixins.cs

+91-31
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Threading;
1212
using Splat;
1313
using System.Reactive.Concurrency;
14+
using System.Linq;
1415

1516
namespace ReactiveUI
1617
{
@@ -212,25 +213,22 @@ public ReactiveDerivedCollection(
212213
this.selector = selector;
213214
this.filter = filter;
214215
this.orderer = orderer;
215-
this.onRemoved = onRemoved;
216+
this.onRemoved = onRemoved ?? (_ => { });
216217
this.signalReset = signalReset;
217218
this.scheduler = scheduler;
218219

219220
this.inner = new CompositeDisposable();
220221
this.indexToSourceIndexMap = new List<int>();
221222
this.sourceCopy = new List<TSource>();
222223

223-
if (this.onRemoved != null)
224-
{
225-
this.inner.Add(Disposable.Create(() =>
226-
{
227-
foreach (var item in this)
228-
{
229-
this.onRemoved(item);
230-
}
231-
}));
232-
}
233-
224+
if (onRemoved != null) {
225+
this.inner.Add(Disposable.Create(() => {
226+
foreach (var item in this) {
227+
this.onRemoved(item);
228+
}
229+
}));
230+
}
231+
234232
this.addAllItemsFromSourceCollection();
235233
this.wireUpChangeNotifications();
236234
}
@@ -382,10 +380,7 @@ void internalReplace(int destinationIndex, TValue newItem)
382380
{
383381
var item = this[destinationIndex];
384382
base.SetItem(destinationIndex, newItem);
385-
if (this.onRemoved != null)
386-
{
387-
onRemoved(item);
388-
}
383+
onRemoved(item);
389384
}
390385

391386
/// <summary>
@@ -590,16 +585,14 @@ protected override void internalClear()
590585
{
591586
indexToSourceIndexMap.Clear();
592587
sourceCopy.Clear();
588+
589+
var items = this.ToArray();
590+
591+
base.internalClear();
593592

594-
if (this.onRemoved != null)
595-
{
596-
foreach (var item in this)
597-
{
598-
onRemoved(item);
599-
}
593+
foreach (var item in items) {
594+
onRemoved(item);
600595
}
601-
602-
base.internalClear();
603596
}
604597

605598
void internalInsertAndMap(int sourceIndex, TValue value)
@@ -615,10 +608,7 @@ protected override void internalRemoveAt(int destinationIndex)
615608
indexToSourceIndexMap.RemoveAt(destinationIndex);
616609
var item = this[destinationIndex];
617610
base.internalRemoveAt(destinationIndex);
618-
if (this.onRemoved != null)
619-
{
620-
onRemoved(item);
621-
}
611+
onRemoved(item);
622612
}
623613

624614
/// <summary>
@@ -889,6 +879,8 @@ public static class ObservableCollectionMixin
889879
/// </summary>
890880
/// <param name="selector">A Select function that will be run on each
891881
/// item.</param>
882+
/// <param name="onRemoved">An action that is called on each item when
883+
/// it is removed.</param>
892884
/// <param name="filter">A filter to determine whether to exclude items
893885
/// in the derived collection.</param>
894886
/// <param name="orderer">A comparator method to determine the ordering of
@@ -902,9 +894,9 @@ public static class ObservableCollectionMixin
902894
public static IReactiveDerivedList<TNew> CreateDerivedCollection<T, TNew, TDontCare>(
903895
this IEnumerable<T> This,
904896
Func<T, TNew> selector,
897+
Action<TNew> onRemoved,
905898
Func<T, bool> filter = null,
906899
Func<TNew, TNew, int> orderer = null,
907-
Action<TNew> onRemoved = null,
908900
IObservable<TDontCare> signalReset = null,
909901
IScheduler scheduler = null)
910902
{
@@ -923,6 +915,74 @@ public static IReactiveDerivedList<TNew> CreateDerivedCollection<T, TNew, TDontC
923915
return new ReactiveDerivedCollection<T, TNew>(This, selector, filter, orderer, onRemoved, reset, scheduler);
924916
}
925917

918+
/// <summary>
919+
/// Creates a collection whose contents will "follow" another
920+
/// collection; this method is useful for creating ViewModel collections
921+
/// that are automatically updated when the respective Model collection
922+
/// is updated.
923+
///
924+
/// Note that even though this method attaches itself to any
925+
/// IEnumerable, it will only detect changes from objects implementing
926+
/// INotifyCollectionChanged (like ReactiveList). If your source
927+
/// collection doesn't implement this, signalReset is the way to signal
928+
/// the derived collection to reorder/refilter itself.
929+
/// </summary>
930+
/// <param name="selector">A Select function that will be run on each
931+
/// item.</param>
932+
/// <param name="filter">A filter to determine whether to exclude items
933+
/// in the derived collection.</param>
934+
/// <param name="orderer">A comparator method to determine the ordering of
935+
/// the resulting collection.</param>
936+
/// <param name="signalReset">When this Observable is signalled,
937+
/// the derived collection will be manually
938+
/// reordered/refiltered.</param>
939+
/// <returns>A new collection whose items are equivalent to
940+
/// Collection.Select().Where().OrderBy() and will mirror changes
941+
/// in the initial collection.</returns>
942+
public static IReactiveDerivedList<TNew> CreateDerivedCollection<T, TNew, TDontCare>(
943+
this IEnumerable<T> This,
944+
Func<T, TNew> selector,
945+
Func<T, bool> filter = null,
946+
Func<TNew, TNew, int> orderer = null,
947+
IObservable<TDontCare> signalReset = null,
948+
IScheduler scheduler = null)
949+
{
950+
return This.CreateDerivedCollection(selector, (Action<TNew>)null, filter, orderer, signalReset, scheduler);
951+
}
952+
953+
/// <summary>
954+
/// Creates a collection whose contents will "follow" another
955+
/// collection; this method is useful for creating ViewModel collections
956+
/// that are automatically updated when the respective Model collection
957+
/// is updated.
958+
///
959+
/// Be aware that this overload will result in a collection that *only*
960+
/// updates if the source implements INotifyCollectionChanged. If your
961+
/// list changes but isn't a ReactiveList/ObservableCollection,
962+
/// you probably want to use the other overload.
963+
/// </summary>
964+
/// <param name="selector">A Select function that will be run on each
965+
/// item.</param>
966+
/// <param name="onRemoved">An action that is called on each item when
967+
/// it is removed.</param>
968+
/// <param name="filter">A filter to determine whether to exclude items
969+
/// in the derived collection.</param>
970+
/// <param name="orderer">A comparator method to determine the ordering of
971+
/// the resulting collection.</param>
972+
/// <returns>A new collection whose items are equivalent to
973+
/// Collection.Select().Where().OrderBy() and will mirror changes
974+
/// in the initial collection.</returns>
975+
public static IReactiveDerivedList<TNew> CreateDerivedCollection<T, TNew>(
976+
this IEnumerable<T> This,
977+
Func<T, TNew> selector,
978+
Action<TNew> onRemoved,
979+
Func<T, bool> filter = null,
980+
Func<TNew, TNew, int> orderer = null,
981+
IScheduler scheduler = null)
982+
{
983+
return This.CreateDerivedCollection(selector, onRemoved, filter, orderer, (IObservable<Unit>)null, scheduler);
984+
}
985+
926986
/// <summary>
927987
/// Creates a collection whose contents will "follow" another
928988
/// collection; this method is useful for creating ViewModel collections
@@ -948,10 +1008,10 @@ public static IReactiveDerivedList<TNew> CreateDerivedCollection<T, TNew>(
9481008
Func<T, TNew> selector,
9491009
Func<T, bool> filter = null,
9501010
Func<TNew, TNew, int> orderer = null,
951-
Action<TNew> onRemoved = null,
9521011
IScheduler scheduler = null)
9531012
{
954-
return This.CreateDerivedCollection(selector, filter, orderer, onRemoved, (IObservable<Unit>)null, scheduler);
1013+
return This.CreateDerivedCollection(selector, (Action<TNew>)null, filter, orderer, (IObservable<Unit>)null, scheduler);
9551014
}
1015+
9561016
}
9571017
}

0 commit comments

Comments
 (0)