/// <summary> /// Creates an observable collection whose content reflects the content of the specified collection, filtered by the specified predicate and mapped by the specified selector. /// This collection is kept up-to-date with respect to changes in the original colletion. /// </summary> public static MyReadOnlyObservableCollection <TResult> WhereSelectLive <T, TResult>(this IReadOnlyObservableCollection <T> collection, Func <T, bool> predicate, Func <T, TResult> selector) { Contract.Requires(collection != null); Contract.Requires(selector != null); var result = new ProperObservableCollection <TResult>(); collection.CollectionChanged += collectionChanged; collectionChanged(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection)); return(new MyReadOnlyObservableCollection <TResult>(result)); void collectionChanged(object sender, NotifyCollectionChangedEventArgs e) { Contract.Requires(sender == collection); switch (e.Action) { case NotifyCollectionChangedAction.Add: result.InsertRange(e.NewStartingIndex, e.NewItems.Cast <T>().Where(predicate).Select(selector)); break; case NotifyCollectionChangedAction.Remove: result.RemoveRange(e.OldStartingIndex, e.OldItems.Count); break; case NotifyCollectionChangedAction.Replace: result.Replace(e.OldStartingIndex, e.OldItems.Count, e.NewItems.Cast <T>().Where(predicate).Select(selector)); break; case NotifyCollectionChangedAction.Move: result.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex); break; case NotifyCollectionChangedAction.Reset: result.Clear(); break; default: throw new ArgumentException("No defined NotifyCollectionChangedEventArgs.Action specified", nameof(e)); } } }
private static void collectionChanged <T, TResult>(NotifyCollectionChangedEventArgs e, ProperObservableCollection <TResult> result, Func <T, TResult> selector) { Contract.Requires(e != null); Contract.Requires(result != null); Contract.Requires(selector != null); switch (e.Action) { case NotifyCollectionChangedAction.Add: if (e.NewStartingIndex == -1) { result.AddRange(e.NewItems.Cast <T>().Select(selector)); } else { result.InsertRange(e.NewStartingIndex, e.NewItems.Cast <T>().Select(selector)); } break; case NotifyCollectionChangedAction.Remove: result.RemoveRange(e.OldStartingIndex, e.OldItems.Count); break; case NotifyCollectionChangedAction.Replace: result.Replace(e.OldStartingIndex, e.OldItems.Count, e.NewItems.Cast <T>().Select(selector)); break; case NotifyCollectionChangedAction.Move: result.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex); break; case NotifyCollectionChangedAction.Reset: result.Clear(); break; default: throw new ArgumentException("No defined NotifyCollectionChangedEventArgs.Action specified", nameof(e)); } }
/// <summary> /// Creates an observable collection whose content reflects the content of the specified collection, filtered by the specified predicate and mapped by the specified many selector. /// This collection is kept up-to-date with respect to changes in the original colletion. /// </summary> public static MyReadOnlyObservableCollection <TResult> WhereSelectManyLive <T, TResult>(this ObservableCollection <T> collection, Func <T, bool> predicate, Func <T, IEnumerable <TResult> > selector) { Contract.Requires(collection != null); Contract.Requires(selector != null); List <int> elementCountsPerSourceElement = new List <int>(); int cumulativeElements(int sourceElementIndex) { return(elementCountsPerSourceElement.Take(sourceElementIndex).Sum()); } var result = new ProperObservableCollection <TResult>(); collection.CollectionChanged += collectionChanged; collectionChanged(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection)); return(new MyReadOnlyObservableCollection <TResult>(result)); void collectionChanged(object sender, NotifyCollectionChangedEventArgs e) { Contract.Requires(sender == collection); switch (e.Action) { case NotifyCollectionChangedAction.Add: { int sourceItemIndex = e.NewStartingIndex; int resultIndex = cumulativeElements(e.NewStartingIndex); foreach (var newSourceItem in e.NewItems.Cast <T>().Where(predicate)) { int originalResultCount = result.Count; result.InsertRange(resultIndex, selector(newSourceItem)); int selectedElementCount = result.Count - originalResultCount; resultIndex += selectedElementCount; elementCountsPerSourceElement.Insert(sourceItemIndex++, selectedElementCount); } break; } case NotifyCollectionChangedAction.Remove: { int resultIndex = cumulativeElements(e.OldStartingIndex); int resultToRemoveCount = elementCountsPerSourceElement.Skip(e.OldStartingIndex).Take(e.OldItems.Count).Sum(); result.RemoveRange(resultIndex, resultToRemoveCount); elementCountsPerSourceElement.RemoveRange(e.OldStartingIndex, e.OldItems.Count); break; } case NotifyCollectionChangedAction.Replace: result.Replace(e.OldStartingIndex, e.OldItems.Count, e.NewItems.Cast <T>().Where(predicate).SelectMany(selector)); break; case NotifyCollectionChangedAction.Move: result.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex); break; case NotifyCollectionChangedAction.Reset: result.Clear(); break; default: throw new ArgumentException("No defined NotifyCollectionChangedEventArgs.Action specified", nameof(e)); } } }