public CustomTotalRows(IObservableCache <Trade, int> source) { /* * This technique can be used to create grid data with dynamic running totals * * [In production systems I also include the ability to have total row expanders - perhaps an example could follow another time] */ //1. create a trade proxy which enriches the trade with an aggregation id var tickers = source.Connect() .ChangeKey(proxy => new AggregationKey(AggregationType.Item, proxy.Id.ToString())) .Transform((trade, key) => new TradeProxy(trade, key)); //2. create grouping based on each ticker var tickerTotals = source.Connect() .GroupWithImmutableState(trade => new AggregationKey(AggregationType.SubTotal, trade.Ticker)) .Transform(grouping => new TradeProxy(grouping.Items.ToArray(), grouping.Key)); //3. create grouping of 1 so we can create grand total row var overallTotal = source.Connect() .GroupWithImmutableState(trade => new AggregationKey(AggregationType.GrandTotal, "All")) .Transform(grouping => new TradeProxy(grouping.Items.ToArray(), grouping.Key)); //4. join all togther so results are in a single cache AggregatedData = tickers.Or(overallTotal) .Or(tickerTotals) .AsObservableCache(); _cleanUp = AggregatedData; }
/// <summary> /// Initializes a new instance of the <see cref="SharedWindowsViewModel"/> class. /// Initializes a new instance of the <see cref="RemoteViewerViewModel"/> class. /// </summary> /// <param name="screen"> /// The screen. /// </param> /// <param name="remoteHost"> /// The favorite. /// </param> /// <param name="shareableWindows"> /// The shareable Windows. /// </param> /// <param name="header"> /// The header. /// </param> public SharedWindowsViewModel(IScreen screen, RemoteHost remoteHost, IObservableCache<ShareableWindow, IntPtr> shareableWindows, string header) { this.HostScreen = screen; this.RemoteHost = remoteHost; this.Header = header; // create proxy objects from source cache to have individual instances per RemoteHost this.cleanUp = shareableWindows.Connect().Transform( window => { // observe IsShared property and update header if necessary var w = new RemoteHostShareableWindow(window); w.WhenPropertyChanged(shareableWindow => shareableWindow.IsShared).Subscribe( shareableWindow => this.RaisePropertyChanged("Header")); w.WhenPropertyChanged(shareableWindow => shareableWindow.IsShared).Subscribe( x => { if (x.Value) RemoteContext.Instance.ShareWindows( remoteHost.Name, this.shareableWindows.Select(sw => new Window() { Id = sw.Handle.ToString(), Name = sw.ProcessName }).ToList()); }); //w.WhenAnyValue(x => x.IsShared).Where(x => x) // .Subscribe(x => RemoteContext.Instance.ShareWindows( // remoteHost.Name, this.shareableWindows.Select(sw => new Window() { Id = sw.Handle.ToString(), Name = sw.ProcessName }).ToList())); return w; }).ObserveOnDispatcher().Bind(out this.shareableWindows).Subscribe(); // Update Header when Number of windows changed, maybe a shared window has been removed this.shareableWindows.WhenPropertyChanged(windows => windows.Count).Subscribe(windows => this.RaisePropertyChanged("Header")); }
public IObservable <IChangeSet <TObject, TKey> > Run() { return(Observable.Create <IChangeSet <TObject, TKey> >(observer => { lock (_locker) if (++_refCount == 1) { _cache = _source.AsObservableCache(); } var subscriber = _cache.Connect().SubscribeSafe(observer); return Disposable.Create(() => { subscriber.Dispose(); IDisposable cacheToDispose = null; lock (_locker) if (--_refCount == 0) { cacheToDispose = _cache; _cache = null; } cacheToDispose?.Dispose(); }); })); }
//private ReadOnlyObservableCollection<SHDObject<T>> _items; public InteractiveCollectionViewModel(IObservableCache <T, R> observable, IObservable <Predicate <T> > visiblefilter, IObservable <Predicate <T> > enabledfilter, IScheduler scheduler, Func <T, IConvertible> getkey = null, string title = null) { disposable = observable.Connect() .ObserveOn(scheduler) .Transform(s => { var so = new SHDObject <T>(s, visiblefilter, enabledfilter, getkey?.Invoke(s)); this.ReactToChanges(so); return((IContain <T>)so); }) .Bind(out _items) .DisposeMany() .Subscribe( _ => { foreach (var x in _.Select(a => new KeyValuePair <IContain <T>, ChangeReason>(a.Current, a.Reason))) { (Changes as Subject <KeyValuePair <IContain <T>, ChangeReason> >).OnNext(x); } }, ex => { (Errors as ISubject <Exception>).OnNext(ex); Console.WriteLine("Error in generic view model"); }, () => Console.WriteLine("observable completed")); Title = title; }
public IObservable <IChangeSet <TObject, TKey> > Run() { return(Observable.Create <IChangeSet <TObject, TKey> >(observer => { Interlocked.Increment(ref _refCount); if (Volatile.Read(ref _refCount) == 1) { Interlocked.Exchange(ref _cache, _source.AsObservableCache()); } // ReSharper disable once PossibleNullReferenceException (never the case!) var subscriber = _cache.Connect().SubscribeSafe(observer); return Disposable.Create(() => { Interlocked.Decrement(ref _refCount); subscriber.Dispose(); if (Volatile.Read(ref _refCount) != 0) { return; } _cache.Dispose(); Interlocked.Exchange(ref _cache, null); }); })); }
protected override RoleViewModel TransformToViewModel(Role model) { var vm = base.TransformToViewModel(model); var permissionsChanges = _permissionsCache.Connect(); var rolePermissions = _domain0.Model.RolePermissions .Connect() .ToCollection(); var combined = Observable.CombineLatest( rolePermissions, permissionsChanges, (rp, _) => rp .Where(x => x.RoleId == vm.Id) .Select(x => _permissionsCache.Lookup(x.Id.Value).Value) ); combined .Subscribe(x => { vm.Permissions = x; vm.PermissionsString = string.Join(",", x.Select(p => p.Name)); }) .DisposeWith(vm.Disposables); return(vm); }
public SelectCacheItem(ISchedulerProvider schedulerProvider) { /* * Example to show how to select an item after after it has been added to a cache and subsequently transformed. * * If a filter is applied before the transform this methodology will not work. In that case, alternatives would be * to: * * 1. Add .Do(changes => some custom logic) after the bind statement * 2 Add .OnItemAdded(i => SelectedItem = i) after the bind statement * * In both these options there is no need to split the source cache into a separate transformed cache */ _schedulerProvider = schedulerProvider; _transformedCache = _sourceCache.Connect() .Transform(si => new TransformedCacheItem(si)) .AsObservableCache(); var binder = _transformedCache.Connect() .ObserveOn(schedulerProvider.MainThread) .Bind(out var data) .Subscribe(); Data = data; _cleanUp = new CompositeDisposable(binder, _transformedCache, _sourceCache); }
/// <summary> /// Initializes a new instance of the <see cref="T:System.Object"/> class. /// </summary> public AnonymousObservableCache(IObservableCache <TObject, TKey> cache) { if (cache == null) { throw new ArgumentNullException(nameof(cache)); } _cache = new ObservableCache <TObject, TKey>(cache.Connect()); }
public DeferUntilLoaded(IObservableCache <TObject, TKey> source) { _result = source.CountChanged.Where(count => count != 0) .Take(1) .Select(_ => new ChangeSet <TObject, TKey>()) .Concat(source.Connect()) .NotEmpty(); }
/// <summary> /// Creates a stream using the specified controlled filter. /// The controlled filter enables dynamic inline changing of the filter. /// </summary> /// <typeparam name="TObject">The type of the object.</typeparam> /// <typeparam name="TKey">The type of the key.</typeparam> /// <param name="source">The source.</param> /// <param name="filterController">The controlled filter.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">filterController</exception> public static IObservable <IChangeSet <TObject, TKey> > Connect <TObject, TKey>(this IObservableCache <TObject, TKey> source, FilterController <TObject> filterController) { if (filterController == null) { throw new ArgumentNullException(nameof(filterController)); } return(source.Connect().Filter(filterController)); }
public LibraryViewModel(IObservableCache <Modpack, Guid> modpacks) { modpacks.Connect() .ObserveOn(RxApp.MainThreadScheduler) .Transform(m => new ModpackViewModel(m)) .Bind(Modpacks) .Subscribe(); LogTo.Verbose("Library initialized"); }
/// <summary> /// Gets a filtered observable of controllers of the <see cref="T">specified type</see> /// </summary> /// <typeparam name="T">The controller type.</typeparam> /// <param name="devices">The devices collection.</param> /// <param name="predicate">The optional predicate to filter controllers.</param> /// <returns>An observable of controllers.</returns> public static IObservable <T> Controllers <T>(this IObservableCache <Device, string> devices, Func <T, bool>?predicate = null) where T : Controller => devices.Connect() .Flatten() .Where(change => change.Reason == ChangeReason.Add) .Select(change => change.Current) .Select(HIDDevices.Controllers.Controller.Create <T>) .Where(controller => controller != null && (predicate is null || predicate(controller)));
/// <summary> /// Connects to the cache, and casts the object to the specified type /// Alas, I had to add the converter due to type inference issues /// </summary> /// <typeparam name="TSource">The type of the object.</typeparam> /// <typeparam name="TKey">The type of the key.</typeparam> /// <typeparam name="TDestination">The type of the destination.</typeparam> /// <param name="source">The source.</param> /// <param name="converter">The conversion factory.</param> /// <returns></returns> public static IObservable <IChangeSet <TDestination, TKey> > Cast <TSource, TKey, TDestination>(this IObservableCache <TSource, TKey> source, Func <TSource, TDestination> converter) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return(source.Connect().Cast(converter)); }
public SearchItemGroup(string title, IObservableCache <ISearchItem, ComposedKey> groupCache) { Title = title; groupCache.Connect() .Bind(out _items) .DisposeMany() .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(); }
public MonitoringGroup(string name, IObservableCache <IMonitoredCall, int> monitoredCalls) { mNameSubject = new BehaviorSubject <string>(name ?? throw new ArgumentNullException(nameof(name))); mCallIds = new SourceCache <int, int>(id => id); WhenNameChanges = mNameSubject.AsObservable(); Calls = monitoredCalls.Connect() .InnerJoin(mCallIds.Connect(), id => id, (call, _) => call) .RemoveKey(); }
/// <summary> /// Gets a filtered observable of controllers of the <see cref="T">specified type</see> /// </summary> /// <typeparam name="T">The controller type.</typeparam> /// <param name="devices">The devices collection.</param> /// <param name="predicate">The optional predicate to filter controllers.</param> /// <returns>An observable of controllers.</returns> public static IObservable <T> Controllers <T>(this IObservableCache <Device, string> devices, Func <T, bool>?predicate = null) where T : Controller => devices.Connect() .Flatten() .Where(change => change.Reason == ChangeReason.Add) .Select(change => change.Current) #pragma warning disable CS8621 // Nullability of reference types in return type doesn't match the target delegate (possibly because of nullability attributes). .Select(HIDDevices.Controllers.Controller.Create <T>) #pragma warning restore CS8621 // Nullability of reference types in return type doesn't match the target delegate (possibly because of nullability attributes). .Where(controller => controller != null && (predicate is null || predicate(controller)));
public MyYearAndWeekAggregations(IObservableCache <MyCalendarEntry, int> source) { _cleanUp = source.Connect() .Group(x => x.DateTime.Year) .Transform(group => new AnnualGrouping(group)) .DisposeMany() .Bind(out var years) .Subscribe(); Years = years; }
public DeferUntilLoaded([NotNull] IObservableCache <TObject, TKey> source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } _result = source.CountChanged.Where(count => count != 0) .Take(1) .Select(_ => new ChangeSet <TObject, TKey>()) .Concat(source.Connect()) .NotEmpty(); }
protected override PermissionViewModel TransformToViewModel(Permission model) { var vm = base.TransformToViewModel(model); _applicationsCache .Connect() .Select(x => _applicationsCache.Lookup(vm.ApplicationId).ValueOrDefault()?.Name) .Subscribe(x => vm.Application = x) .DisposeWith(vm.Disposables); return(vm); }
protected override MessageTemplateViewModel TransformToViewModel(MessageTemplate model) { var vm = base.TransformToViewModel(model); _environmentsCache .Connect() .Select(_ => _environmentsCache.Lookup(vm.EnvironmentId).ValueOrDefault()?.Name) .Subscribe(x => vm.Environment = x) .DisposeWith(vm.Disposables); return(vm); }
public FlattenNestedObservableCollection(IObservableCache <ClassWithNestedObservableCollection, int> source) { /* * Create a flat cache based on a nested observable collection. * * Since a new changeset is produced each time a parent is added, I recommend applying Batch() * after TransformMany() to reduce notifications (particularly on initial load) */ Children = source.Connect() .TransformMany(parent => parent.Children.ToObservableChangeSet(c => c.Name)) .AsObservableCache(); }
/// <summary> /// print changed curPositionPerClientCache /// </summary> public void PrintcurPositionPerClientCache() { curPositionPerClientCache.Connect() .Subscribe( c => { foreach (var item in c) { LogInfo("curPositionPerClientCache:|" + item.Reason.ToString() + " | " + item.Current.ToString()); } } ); }
private IDisposable LogChanges() { //todo: Move this to a log writing service const string messageTemplate = "{0} {1} {2} ({4}). Status = {3}"; return(_all.Connect().SkipInitial() .Transform(trade => string.Format(messageTemplate, trade.BuyOrSell, trade.Amount, trade.CurrencyPair, trade.Status, trade.Customer)) .Subscribe(changes => changes.ForEach(change => _logger.Info(change.Current)))); }
private IDisposable LogChanges() { const string messageTemplate = "{0} {1} {2} ({4}). Status = {3}"; return(_all.Connect().Skip(1) .WhereReasonsAre(ChangeReason.Add, ChangeReason.Update) .Convert(trade => string.Format(messageTemplate, trade.BuyOrSell, trade.Amount, trade.CurrencyPair, trade.Status, trade.Customer)) .ForEachChange(change => _logger.Info(change.Current)) .Subscribe()); }
public MailFolderSelectionViewModel(IObservableCache <MailFolder, string> folders) { folders.Connect() .ObserveOn(RxApp.TaskpoolScheduler) .Filter(this.WhenAnyValue(x => x.IncludeRoot).Select <bool, Func <MailFolder, bool> >( includeRoot => f => includeRoot || f.Type != FolderType.Root)) .Sort(SortExpressionComparer <MailFolder> .Ascending(f => f.Type).ThenByAscending(f => f.Name)) .TransformToTree(f => f.ParentId) .Transform(n => new MailFolderSelectionItem(n)) .DisposeMany() .ObserveOn(RxApp.MainThreadScheduler) .Bind(out _folders) .Subscribe() .DisposeWith(_disposables); }
public AutoRefreshForPropertyChanges(IObservableCache <MutableThing, int> dataSource) { /* * The observable cache has no concept of mutable properties. It only knows about adds, updates and removes. * However it does have the concept of a refresh, which is a manual way to tell downstream operators like * distinct, sort, filter and grouping to re-evaluate * * To force a refresh, you need to manually trigger when to do so. In this case property changes are monitored * and the data source sends a refresh signal to all downstream operators. */ DistinctCount = dataSource.Connect() .AutoRefresh(t => t.Value) //Omit param to refresh for any property .DistinctValues(m => m.Value) .Count(); }
public EndpointGroupViewModel( EndpointGroup group, IObservable <IChangeSet <NodeInputViewModel> > allInputs, IObservable <IChangeSet <NodeOutputViewModel> > allOutputs, IObservableCache <Node <EndpointGroup, EndpointGroup>, EndpointGroup> children, EndpointGroupViewModelFactory endpointGroupViewModelFactory) { Group = group; VisibleInputs = allInputs.Filter(e => e.Group == group).AsObservableList(); VisibleOutputs = allOutputs.Filter(e => e.Group == group).AsObservableList(); children .Connect() .Transform(n => endpointGroupViewModelFactory(n.Key, allInputs, allOutputs, n.Children, endpointGroupViewModelFactory)) .Bind(out _children) .Subscribe(); }
public CustomBinding(IObservableCache <Animal, string> source) { /* * Sometimes the default binding does not behave exactly as you want. * Using VariableThresholdObservableCollectionAdaptor is an example of how you can inject your own behaviour. */ Threshold = 5; _cleanUp = source.Connect() .Sort(SortExpressionComparer <Animal> .Ascending(a => a.Name)) .Bind(out var data, adaptor: new VariableThresholdObservableCollectionAdaptor <Animal, string>(() => Threshold)) .Subscribe(); Data = data; }
public SearchCollection(ISearchInfoCollection searchInfoCollection, ISchedulerProvider schedulerProvider) { _viewModels = searchInfoCollection.Searches.Connect() .Transform(tail => new SearchViewModel(tail, vm => { searchInfoCollection.Remove(vm.Text); })) .DisposeMany() .AsObservableCache(); var shared = _viewModels.Connect();//.Publish(); var binderLoader = shared .Sort(SortExpressionComparer <SearchViewModel> .Ascending(tvm => tvm.SearchType == SearchType.All ? 1:2) .ThenByAscending(tvm => tvm.Text)) .ObserveOn(schedulerProvider.MainThread) .Bind(out _items) .Subscribe(); var autoSelector = shared.WhereReasonsAre(ChangeReason.Add) .Flatten() .Select(change => change.Current) .Subscribe(latest => Selected = latest); var removed = shared.WhereReasonsAre(ChangeReason.Remove) .Subscribe(_ => Selected = _viewModels.Items.First()); var counter = shared.ToCollection() .Subscribe(count => Count = count.Count); SelectedText = this.WhenValueChanged(sc => sc.Selected) .Where(x => x != null) .Select(svm => svm.Text) .Replay(1).RefCount(); Latest = this.WhenValueChanged(sc => sc.Selected) .Where(x => x != null) .Select(svm => svm.Latest) .Switch() .Replay(1).RefCount(); _cleanUp = new CompositeDisposable(_viewModels, binderLoader, counter, removed, autoSelector); }
public SearchCollection(ISearchInfoCollection searchInfoCollection, ISchedulerProvider schedulerProvider) { _viewModels = searchInfoCollection.Searches.Connect() .Transform(tail => new SearchViewModel(tail, vm => { searchInfoCollection.Remove(vm.Text); })) .DisposeMany() .AsObservableCache(); var shared = _viewModels.Connect();//.Publish(); var binderLoader = shared .Sort(SortExpressionComparer<SearchViewModel> .Ascending(tvm => tvm.SearchType== SearchType.All ? 1:2) .ThenByAscending(tvm => tvm.Text)) .ObserveOn(schedulerProvider.MainThread) .Bind(out _items) .Subscribe(); var autoSelector = shared.WhereReasonsAre(ChangeReason.Add) .Flatten() .Select(change => change.Current) .Subscribe(latest => Selected = latest); var removed = shared.WhereReasonsAre(ChangeReason.Remove) .Subscribe(_ => Selected = _viewModels.Items.First()); var counter = shared.ToCollection() .Subscribe(count => Count = count.Count); SelectedText = this.WhenValueChanged(sc => sc.Selected) .Where(x => x != null) .Select(svm => svm.Text) .Replay(1).RefCount(); Latest = this.WhenValueChanged(sc => sc.Selected) .Where(x=>x!=null) .Select(svm => svm.Latest) .Switch() .Replay(1).RefCount(); _cleanUp = new CompositeDisposable(_viewModels, binderLoader, counter, removed, autoSelector); }
public IDisposable Switch(bool useGroup) { SourceList.Clear(); return(Choose() .Subscribe(a => { SourceList.EditDiff(a); })); IObservable <IEnumerable <object> > Choose() { return(useGroup == false ? ungrouped .Connect() .ToCollection() : grouped .Connect() .ToCollection()); } }
public BaseSummaryService(IObservableCache <Stock, T> stocks, IObservableCache <Stock, TK> fundStocks, IScheduler scheduler) { if (stocks == null || fundStocks == null) { throw new NotSupportedException("stocks or fundStocks null parametr not supported"); } var all = stocks.Connect(); Observable.ObserveOn(all.Count(), scheduler).Subscribe(x => TotalNumber = x); Observable.ObserveOn(all.Sum(x => new StockProxy(x).MarketValue), scheduler).Subscribe(x => TotalMarketValue = x); Observable.ObserveOn(fundStocks.Connect().Sum(x => new StockProxy(x).MarketValue), scheduler).Subscribe(x => { TotalStockWeight = all.Sum(item => new StockProxy(item) { TotalMarketValue = x }.StockWeight).ToProperty(this, item => item.TotalStockWeight).Value; }); }