/// <summary> /// Creates a collection whose contents will "follow" another /// collection; this method is useful for creating ViewModel collections /// that are automatically updated when the respective Model collection /// is updated. /// </summary> /// <param name="selector">A Select function that will be run on each /// item.</param> /// <returns>A new collection whose items are equivalent to /// Collection.Select(selector) and will mirror the initial collection.</returns> public static ReactiveCollection <TNew> CreateDerivedCollection <T, TNew>( this ObservableCollection <T> This, Func <T, TNew> selector) { Contract.Requires(selector != null); #if !IOS // Contract.Result is borked in Mono Contract.Ensures(Contract.Result <ReactiveCollection <TNew> >().Count == This.Count); #endif var ret = new ReactiveCollection <TNew>(This.Select(selector)); var coll_changed = new Subject <NotifyCollectionChangedEventArgs>(); This.CollectionChanged += (o, e) => coll_changed.OnNext(e); /* XXX: Ditto as from above * var coll_changed = Observable.FromEvent<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>( * x => This.CollectionChanged += x, x => This.CollectionChanged -= x); */ coll_changed.Subscribe(x => { switch (x.Action) { case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Replace: // NB: SL4 fills in OldStartingIndex with -1 on Replace :-/ int old_index = (x.Action == NotifyCollectionChangedAction.Replace ? x.NewStartingIndex : x.OldStartingIndex); if (x.OldItems != null) { foreach (object _ in x.OldItems) { ret.RemoveAt(old_index); } } if (x.NewItems != null) { foreach (T item in x.NewItems.Cast <T>()) { ret.Insert(x.NewStartingIndex, selector(item)); } } break; case NotifyCollectionChangedAction.Reset: ret.Clear(); break; default: break; } }); return(ret); }