public InventoryViewModel(IObservableList <Shop> shops) { shops.Connect() .TransformMany(s => s.Products.Select(p => new InventoryItemViewModel(s, p)), new InventoryItemViewModelEqualityComparer()) .Bind(out inventoryItems) .Subscribe(); }
public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { Interlocked.Increment(ref _refCount); if (Volatile.Read(ref _refCount) == 1) { Interlocked.Exchange(ref _list, _source.AsObservableList()); } // ReSharper disable once PossibleNullReferenceException (never the case!) var subscriber = _list.Connect().SubscribeSafe(observer); return Disposable.Create(() => { Interlocked.Decrement(ref _refCount); subscriber.Dispose(); if (Volatile.Read(ref _refCount) != 0) { return; } _list.Dispose(); Interlocked.Exchange(ref _list, null); }); })); }
public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { lock (_locker) if (++_refCount == 1) { _list = _source.AsObservableList(); } var subscriber = _list.Connect().SubscribeSafe(observer); return Disposable.Create(() => { subscriber.Dispose(); IDisposable listToDispose = null; lock (_locker) if (--_refCount == 0) { listToDispose = _list; _list = null; } listToDispose?.Dispose(); }); })); }
public IObservableListBindCacheSortedFixture() { _source = new SourceCache <Person, string>(p => p.Name); _sourceCacheNotifications = _source.Connect().AutoRefresh().Sort(_comparer, resetThreshold: 10).BindToObservableList(out _list).AsAggregator(); _listNotifications = _list.Connect().AsAggregator(); }
public PagingListWithVirtualise(IObservableList <Animal> source, IObservable <IVirtualRequest> requests) { Virtualised = source.Connect() .Virtualise(requests) .AsObservableList(); _cleanUp = Virtualised; }
public SimplePagging(IObservableList <Animal> source, IObservable <IPageRequest> pager) { Paged = source.Connect() .Page(pager) .Do(changes => Console.WriteLine(changes.TotalChanges)) //added as a quick and dirty way to debug .AsObservableList(); _cleanUp = Paged; }
public StaticFilter(IObservableList <Animal> source) { //this list will automatically filter by Mammals only when the underlying list receives adds, or removes Mammals = source.Connect() .Filter(animal => animal.Family == AnimalFamily.Mammal) .AsObservableList(); _cleanUp = Mammals; }
public IObservableListBindListFixture() { _source = new SourceList <Person>(); _sourceListNotifications = _source .Connect() .AutoRefresh() .BindToObservableList(out _list) .AsAggregator(); _observableListNotifications = _list.Connect().AsAggregator(); }
public DynamicFilter(IObservableList <Animal> source, ISchedulerProvider schedulerProvider) { //produce an observable which creates a new predicate whenever AnimalFilter property changes var dynamicFilter = this.WhenValueChanged(@this => @this.AnimalFilter) .Throttle(TimeSpan.FromMilliseconds(250), schedulerProvider.Background) //throttle to prevent constant filtering (i.e. when users type) .Select(CreatePredicate); //Create list which automatically filters when AnimalFilter changes Filtered = source.Connect() .Filter(dynamicFilter) //dynamicfilter can accept any predicate observable (i.e. does not have to be based on a property) .AsObservableList(); _cleanUp = Filtered; }
public SimulationListViewModel(IObservableList <SimulationRowViewModel> simulations, ReactiveUI.ReactiveCommand renderCommand) { if (simulations == null) { throw new ArgumentNullException(nameof(simulations)); } simulations .Connect() .Bind(Simulations) .Subscribe(); RenderCommand = renderCommand ?? throw new ArgumentNullException(nameof(renderCommand)); }
public PropertyFilter(IObservableList <Animal> source, ISchedulerProvider schedulerProvider) { /* * Create list which automatically filters: * * a) When the underlying list changes * b) When IncludeInResults property changes * c) NB: Add throttle when IncludeInResults properties can change in multiple animals in quick sucession * (i.e. each time the prop changes the filter is re-assessed potentially leading to a flurry of updates - better to slow that down) */ Filtered = source.Connect() .FilterOnProperty(animal => animal.IncludeInResults, animal => animal.IncludeInResults, TimeSpan.FromMilliseconds(250), schedulerProvider.Background) .AsObservableList(); _cleanUp = Filtered; }
public ExternalSourceFilter(IObservableList <Animal> source, IObservableList <AnimalFamily> families) { /* * Create list which is filtered from the result of another filter */ var familyFilter = families.Connect() .ToCollection() .Select(items => { bool Predicate(Animal animal) => items.Contains(animal.Family); return((Func <Animal, bool>)Predicate); }); Filtered = source.Connect() .Filter(familyFilter) .AsObservableList(); _cleanUp = Filtered; }
public XamarinFormsGrouping(IObservableList <Animal> source, ISchedulerProvider schedulerProvider) { /* Xamarin forms is a bit dumb and cannot handle nested observable collections. * To cirumvent this limitation, create a specialist observable collection with headers and use dynamic data to manage it */ //create an observable predicate var observablePredicate = this.WhenValueChanged(@this => @this.Filter).ObserveOn(schedulerProvider.Background); _cleanUp = source.Connect() .Filter(observablePredicate) //Apply filter dynamically .GroupOn(arg => arg.Family) //create a dynamic group .Transform(grouping => new AnimalGroup(grouping, schedulerProvider)) //transform into a specialised observable collection .Sort(SortExpressionComparer <AnimalGroup> .Ascending(a => a.Family)) .ObserveOn(schedulerProvider.MainThread) .Bind(out var animals) .DisposeMany() //use DisposeMany() because the grouping is disposable .Subscribe(); FamilyGroups = animals; }
public ChangeComparer(IObservableList <int> source) { /* * Pass IObservable<IComparer<T>> into the sort operator to switch sorting * * The same concept applies to the ObservableCache */ var optionChanged = this.WhenValueChanged(@this => @this.Option) .Select(opt => opt == ChangeComparereOption.Ascending ? SortExpressionComparer <int> .Ascending(i => i) : SortExpressionComparer <int> .Descending(i => i)); //create a sorted observable list DataSource = source.Connect() .Sort(optionChanged) .AsObservableList(); _cleanUp = DataSource; }
public DataGridViewModel(List <DataTableColumn> dataColumns, IObservableList <DataTableRow> data, OutputModeOption outputMode) { var columns = dataColumns.Select(x => new Column(x)); Columns.AddRange(columns); SelectionMode = OutputModeToSelectionMode(outputMode); this.WhenActivated((CompositeDisposable disposables) => { Columns.Connect() .AutoRefresh() .ObserveOn(RxApp.MainThreadScheduler) .Bind(out _columnSelect) .Subscribe(); data.Connect() .Bind(out _viewObjects) .Subscribe(); }); }
public DerivedProgressionManager(IObservableList <IProgressionViewer> subProgs, ILoggerFactory loggerFactory) { this.logger = loggerFactory.CreateLogger <DerivedProgressionManager>(); subProgs .Connect() .ToCollection() .Select(progs => progs.Select(prog => prog.WhenAnyValue( vm => vm.Title, vm => vm.State, vm => vm.Target, vm => vm.Current, vm => vm.Details, vm => vm.Weight, ProgressionSnapshot.Factory))) .Select(Observable.CombineLatest) .Switch() .Subscribe(progs => { this.State = JobStateHelper.MergeStates(progs.Select(prog => prog.State)); Target = progs.Sum(prog => prog.Weight) * Resolution; Current = (int)progs.Sum(prog => (decimal)prog.Current / (decimal)prog.Target * Weight * Resolution); var newDetails = progs.FirstOrDefault(x => x.State == JobState.InProgress).Details; if (State.IsIn(JobState.ToDo, JobState.Done)) { newDetails = string.Empty; } if (State == JobState.Failed) { newDetails = $"Job Failed.{Environment.NewLine + progs.FirstOrDefault(x => x.State == JobState.Failed).Details}"; logger.LogError("job failed: {0}", newDetails); } Details = newDetails; logger.LogTrace("extra info:" + subProgs.Count + Environment.NewLine + string.Join(Environment.NewLine, progs.Select(prog => $" {prog.Title} | {prog.State} | {prog.Current}/{prog.Target} : {prog.Details}")) ); logger.LogTrace("updating progress - {0}subs: {1}/{2} ({3}): {4}", progs.Count(), Current, Target, State, Details); }); }
public void SetTagpool(IObservableList <Tag> tagpool) { tagpoolReset.OnNext(new Unit()); Tagpool = tagpool; Tagpool .Connect() .Filter( NewTagChanges.Select <string, Func <Tag, bool> >( newTag => tag => ((string)tag).ToLower().Contains(newTag.ToLower()) ) ) .Except(TagpoolExceptions) .Transform(tag => new TagEx(tag, true)) .Merge( CompletePoolChanges .DistinctUntilChanged() .Select(completePool => completePool ? allTags .Except( TagpoolExceptions.Merge( Selection.Connect())) .Transform(tag => new TagEx(tag, false)) : Observable.Empty <IChangeSet <TagEx> >() ) .Switch() ) //.Sort() .ObserveOn(RxApp.MainThreadScheduler) .Bind(TagpoolBinding) .TakeUntil(tagpoolReset) .TakeUntil(destroy) .Subscribe(_ => { }, ex => { MessageBox.Show($"({GetType().FullName}) tagpool exception: " + Environment.NewLine + ex.ToString()); }); }
public Aggregations(IObservableList <int> source) { /* * Available aggregations: Max, Min, Avg, StdDev, Count, Sum. * * For custom aggregations use: source.Connect().ToCollection().Select(items=>...); */ var shared = source.Connect() //by default dd never notifies when the change set is empty i.e. upon subscripion when the source has no data //this means that no result is computed until data is loaded. However if you require a result even when the data source is empty, use StartWithEmpty() .StartWithEmpty() //use standard rx Publish() / Connect() to share published changesets .Publish(); _cleanUp = new CompositeDisposable ( shared.Maximum(i => i).Subscribe(max => Max = max), shared.Minimum(i => i).Subscribe(min => Min = min), shared.Avg(i => i).Subscribe(avg => Avg = avg), shared.Connect() ); }
public IObservable <IChangeSet <T> > Run() { int refCount = 0; var locker = new object(); IObservableList <T> list = null; return(Observable.Create <IChangeSet <T> >(observer => { lock (locker) { refCount++; if (refCount == 1) { list = _source.AsObservableList(); } // ReSharper disable once PossibleNullReferenceException (never the case!) var subscriber = list.Connect().SubscribeSafe(observer); return Disposable.Create(() => { lock (locker) { refCount--; subscriber.Dispose(); if (refCount != 0) { return; } list.Dispose(); list = null; } }); } })); }
public IObservable <IChangeSet <TObject, TKey> > Run() { return(Observable.Create <IChangeSet <TObject, TKey> >( observer => { var locker = new object(); // this is the resulting cache which produces all notifications var resultCache = new ChangeAwareCache <TObject, TKey>(); // Transform to a merge container. // This populates a RefTracker when the original source is subscribed to var sourceLists = _source.Connect().Synchronize(locker).Transform(changeSet => new MergeContainer(changeSet)).AsObservableList(); var sharedLists = sourceLists.Connect().Publish(); // merge the items back together var allChanges = sharedLists.MergeMany(mc => mc.Source).Synchronize(locker).Subscribe( changes => { // Populate result list and check for changes UpdateResultList(resultCache, sourceLists.Items.AsArray(), changes); var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) { observer.OnNext(notifications); } }); // when an list is removed, need to var removedItem = sharedLists.OnItemRemoved( mc => { // Remove items if required ProcessChanges(resultCache, sourceLists.Items.AsArray(), mc.Cache.KeyValues); if (_type == CombineOperator.And || _type == CombineOperator.Except) { var itemsToCheck = sourceLists.Items.SelectMany(mc2 => mc2.Cache.KeyValues); ProcessChanges(resultCache, sourceLists.Items.AsArray(), itemsToCheck); } var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) { observer.OnNext(notifications); } }).Subscribe(); // when an list is added or removed, need to var sourceChanged = sharedLists.WhereReasonsAre(ListChangeReason.Add, ListChangeReason.AddRange).ForEachItemChange( mc => { ProcessChanges(resultCache, sourceLists.Items.AsArray(), mc.Current.Cache.KeyValues); if (_type == CombineOperator.And || _type == CombineOperator.Except) { ProcessChanges(resultCache, sourceLists.Items.AsArray(), resultCache.KeyValues.ToArray()); } var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) { observer.OnNext(notifications); } }).Subscribe(); return new CompositeDisposable(sourceLists, allChanges, removedItem, sourceChanged, sharedLists.Connect()); })); }
public async Task TestFilterOnProperty() { var listA = new SourceList <X>(); var listB = new SourceList <X>(); using (IObservableList <X> list = listA.Connect() .Or(listB.Connect()) .AsObservableList()) { var nameA1 = "A1"; var a1 = new X(nameA1); var a2 = new X("A2"); listA.Edit(l => { l.Clear(); l.Add(a1); l.Add(a2); }); var b1 = new X("B1"); var b2 = new X("B2"); listB.Edit(l => { l.Clear(); l.Add(b1); l.Add(b2); }); Assert.AreEqual(4, list.Count); int count = await list.CountChanged.FirstAsync(); Assert.AreEqual(4, count); IObservable <IChangeSet <X> > obsFiltered = list.Connect() .FilterOnProperty(v => v.IsConnected, v => v.IsConnected); using (IObservableList <X> obsFilteredAsList = obsFiltered.AsObservableList()) { IObservable <IChangeSet <XVm> > obsTransformed = obsFiltered .Transform(v => new XVm(v)) .DisposeMany(); var ctorCount = 0; var dtorCount = 0; using (IObservableList <XVm> obsTransformedAsList = obsTransformed.AsObservableList()) { ctorCount += 4; Assert.That(obsFilteredAsList.Items.Contains(a1)); Assert.That(obsFilteredAsList.Items.Count(), Is.EqualTo(obsTransformedAsList.Items.Count())); a1.IsConnected = false; Assert.That(obsFilteredAsList.Items, Has.No.Member(a1)); dtorCount++; Assert.That(XVm.Constructed, Is.EqualTo(ctorCount)); Assert.That(XVm.Destructed, Is.EqualTo(dtorCount)); a1.IsConnected = true; Assert.That(obsFilteredAsList.Items, Has.Member(a1)); ctorCount++; Assert.That(XVm.Constructed, Is.EqualTo(ctorCount)); Assert.That(XVm.Destructed, Is.EqualTo(dtorCount)); Console.WriteLine("--remove"); listA.Remove(a1); dtorCount++; Assert.That(XVm.Constructed, Is.EqualTo(ctorCount)); Assert.That(XVm.Destructed, Is.EqualTo(dtorCount)); Console.WriteLine("--add"); listA.Add(a1); ctorCount++; Assert.That(XVm.Constructed, Is.EqualTo(ctorCount)); Assert.That(XVm.Destructed, Is.EqualTo(dtorCount)); Console.WriteLine("--clear"); listA.Clear(); dtorCount += 2; //FIX: List A contains 2 items (was adding 4) Assert.That(XVm.Constructed, Is.EqualTo(ctorCount)); Assert.That(XVm.Destructed, Is.EqualTo(dtorCount)); Console.WriteLine("--add"); //FIX: Maybe a debate required! List B already contains b1, so not regarded as a new item in the Or result Debug.Assert(listB.Items.Contains(b1)); listA.Add(b1); // ctorCount++; Assert.That(XVm.Constructed, Is.EqualTo(ctorCount)); Assert.That(XVm.Destructed, Is.EqualTo(dtorCount)); Console.WriteLine("--disp"); } dtorCount += 2; //FIX: Should be +3 s this is what Assert.That(XVm.Constructed, Is.EqualTo(ctorCount)); Assert.That(XVm.Destructed, Is.EqualTo(dtorCount)); } } }