public SampleViewModel() { Items = new ReactiveList <SampleObject> (); var rand = new Random(); Observable.Interval(TimeSpan.FromMilliseconds(300)) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(num => { var idx = rand.Next(Items.Count); var act = rand.NextDouble(); if (act < 0.4 || idx >= Items.Count) { Items.Insert( idx, new SampleObject() { Name = "Hallo Number " + num }); Debug.WriteLine("Inserted " + idx); } else if (act < 0.8) { Items [idx].Name = "Now I'm Number " + num; Debug.WriteLine("Renamed " + idx); } else { Items.RemoveAt(idx); Debug.WriteLine("Removed " + idx); } }); }
/// <summary> /// Takes each list produced by this observable and mirrors its contents in the target list. /// The target list is modified, not replaced. /// The type of the target list property is IReadOnlyReactiveList because it doesn't make sense to have a mutible list /// if this binding keeps changing the contents of the list, but the type of the actual object should be ReactiveList /// so the list can be modified by this binding. /// </summary> /// <typeparam name="TObj">The type of viewmodel</typeparam> /// <typeparam name="TListItem">The type of object contained in the list</typeparam> /// <param name="data">The observable to take lists from.</param> /// <param name="target">The viewmodel that is used as a base for finding the target list property</param> /// <param name="property">The IReactiveList property that will be modified.</param> /// <returns>A disposable to break the binding</returns> public static IDisposable BindListContents <TObj, TListItem>(this IObservable <IList <TListItem> > data, TObj target, Expression <Func <TObj, IReadOnlyReactiveList <TListItem> > > property) where TObj : class { IObservable <IReadOnlyReactiveList <TListItem> > targetListObservable = target.WhenAnyValue(property); return(Observable.CombineLatest(targetListObservable, data, (a, b) => (TargetList: a, SourceList: b)) .Subscribe(t => { IReactiveList <TListItem> latestTargetList = t.TargetList as IReactiveList <TListItem>; IList <TListItem> latestData = t.SourceList; if (latestTargetList == null) { return; } if (latestData == null) { latestTargetList.Clear(); return; } var changes = LongestCommonSubsequence.GetChanges(latestTargetList, latestData).ToArray(); if (changes.Length == 0) { return; } using (changes.Length > 1 ? latestTargetList.SuppressChangeNotifications() : Disposable.Empty) { foreach (var(index, item, changeType) in changes) { if (changeType == LongestCommonSubsequence.ChangeType.Removed) { latestTargetList.RemoveAt(index); } else if (changeType == LongestCommonSubsequence.ChangeType.Added) { latestTargetList.Insert(index, item); } } } })); }