/// <summary> /// Converts a <see cref="CollectionNotification{T}"/> to a list of <see cref="CollectionModification{T}"/>. /// </summary> /// <typeparam name="T">The object that provides notification information.</typeparam> /// <param name="notification">The <see cref="CollectionNotification{T}"/> to be converted.</param> /// <returns>A list of <see cref="CollectionModification{T}"/> containing /// <see cref="CollectionModificationKind.Add"/> when the specified <paramref name="notification"/> is <see cref="CollectionNotificationKind.Exists"/> or <see cref="CollectionNotificationKind.OnAdded"/>, /// <see cref="CollectionModificationKind.Remove"/> followed by <see cref="CollectionModificationKind.Add"/> when the specified <paramref name="notification"/> is <see cref="CollectionNotificationKind.OnReplaced"/>, /// <see cref="CollectionModificationKind.Remove"/> when the specified <paramref name="notification"/> is <see cref="CollectionNotificationKind.OnRemoved"/>, or /// <see cref="CollectionModificationKind.Clear"/> when the specified <paramref name="notification"/> is <see cref="CollectionNotificationKind.OnCleared"/>.</returns> public static IList <CollectionModification <T> > ToModifications <T>(this CollectionNotification <T> notification) { Contract.Requires(notification != null); Contract.Ensures(Contract.Result <IList <CollectionModification <T> > >() != null); Contract.Ensures(Contract.Result <IList <CollectionModification <T> > >().IsReadOnly); var list = new List <CollectionModification <T> >(); switch (notification.Kind) { case CollectionNotificationKind.Exists: list.Add(CollectionModification.CreateAdd <T>(notification.ExistingValues)); break; case CollectionNotificationKind.OnAdded: list.Add(CollectionModification.CreateAdd <T>(notification.Value)); break; case CollectionNotificationKind.OnRemoved: list.Add(CollectionModification.CreateRemove <T>(notification.Value)); break; case CollectionNotificationKind.OnReplaced: list.Add(CollectionModification.CreateRemove <T>(notification.ReplacedValue)); list.Add(CollectionModification.CreateAdd <T>(notification.Value)); break; case CollectionNotificationKind.OnCleared: list.Add(CollectionModification.CreateClear <T>()); break; } IList <CollectionModification <T> > result = list.AsReadOnly(); Contract.Assume(result.IsReadOnly); return(result); }
/// <summary> /// Converts <see cref="INotifyCollectionChanged.CollectionChanged"/> events into an observable sequence of <see cref="CollectionModification{T}"/>. /// </summary> /// <typeparam name="T">The object that provides notification information.</typeparam> /// <param name="source">An implementation of <see cref="INotifyCollectionChanged"/> that raises events when a collection changes.</param> /// <remarks> /// An <see cref="NotifyCollectionChangedAction.Add"/> event is projected into zero or more <see cref="CollectionModificationKind.Add"/> modifications. /// A <see cref="NotifyCollectionChangedAction.Remove"/> event is projected into zero or more <see cref="CollectionModificationKind.Remove"/> modifications. /// A <see cref="NotifyCollectionChangedAction.Move"/> event is ignored. /// A <see cref="NotifyCollectionChangedAction.Replace"/> event is projected into zero or more sequential /// <see cref="CollectionModificationKind.Remove"/> and <see cref="CollectionModificationKind.Add"/> modifications. /// A <see cref="NotifyCollectionChangedAction.Reset"/> event is projected into a single <see cref="CollectionModificationKind.Clear"/> modification. /// </remarks> /// <returns>An observable sequence of <see cref="CollectionModification{T}"/> objects corresponding to raised events.</returns> #else /// <summary> /// Converts <see cref="INotifyCollectionChanged.CollectionChanged"/> events into an observable sequence of <see cref="CollectionModification{T}"/>. /// </summary> /// <typeparam name="T">The object that provides notification information.</typeparam> /// <param name="source">An implementation of <see cref="INotifyCollectionChanged"/> that raises events when a collection changes.</param> /// <remarks> /// An <see cref="NotifyCollectionChangedAction.Add"/> event is projected into zero or more <see cref="CollectionModificationKind.Add"/> modifications. /// A <see cref="NotifyCollectionChangedAction.Remove"/> event is projected into zero or more <see cref="CollectionModificationKind.Remove"/> modifications. /// A <see cref="NotifyCollectionChangedAction.Replace"/> event is projected into zero or more sequential /// <see cref="CollectionModificationKind.Remove"/> and <see cref="CollectionModificationKind.Add"/> modifications. /// A <see cref="NotifyCollectionChangedAction.Reset"/> event is projected into a single <see cref="CollectionModificationKind.Clear"/> modification. /// </remarks> /// <returns>An observable sequence of <see cref="CollectionModification{T}"/> objects corresponding to raised events.</returns> #endif public static IObservable <CollectionModification <T> > AsCollectionModifications <T>(this INotifyCollectionChanged source) { Contract.Requires(source != null); Contract.Ensures(Contract.Result <IObservable <CollectionModification <T> > >() != null); return(Observable.FromEventPattern <NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>( eh => source.CollectionChanged += eh, eh => source.CollectionChanged -= eh) .SelectMany(e => { var args = e.EventArgs; switch (args.Action) { case NotifyCollectionChangedAction.Add: return Observable.Return(CollectionModification.CreateAdd(EnsureList <T>(args.NewItems))); case NotifyCollectionChangedAction.Remove: return Observable.Return(CollectionModification.CreateRemove(EnsureList <T>(args.OldItems))); #if !SILVERLIGHT case NotifyCollectionChangedAction.Move: return Observable.Empty <CollectionModification <T> >(); #endif case NotifyCollectionChangedAction.Replace: return Observable.Return(CollectionModification.CreateRemove(EnsureList <T>(args.OldItems))) .Concat(Observable.Return(CollectionModification.CreateAdd(EnsureList <T>(args.NewItems)))); case NotifyCollectionChangedAction.Reset: return Observable.Return(CollectionModification.CreateClear <T>()); default: throw new InvalidOperationException(); } })); }