create observable<bool> from observablecollection - c#

I have ObservableCollection<T> and I need to create observable<bool> which returns true if collection contains any elements
I try to do this
var collectionHasElementsObservable =
Observable.FromEventPattern<NotifyCollectionChangedEventHandler,NotifyCollectionChangedEventArgs>(
ev => ((ObservableCollection<MyType>)_items).CollectionChanged += ev,
ev => ((ObservableCollection<MyType>)_items).CollectionChanged -= ev);
But I don't know how to convert this into IObservable<bool>
How can I create observable<bool> from this?

You can use Select to map the event into one of having elements:
ObservableCollection<int> coll = new ObservableCollection<int>();
var hasElements =
Observable.FromEventPattern<NotifyCollectionChangedEventHandler,NotifyCollectionChangedEventArgs>(
a => coll.CollectionChanged += a,
a => coll.CollectionChanged -= a)
.Select(_ => coll.Count > 0);
Example:
hasElements.Subscribe(Console.WriteLine);
coll.Add(1);
coll.Add(2);
coll.Remove(1);
coll.Remove(2);
Output:
True
True
True
False
Is this what you were looking for?

I notice you have the ReactiveUI tag - were you to be using ReactiveCollection, this would be even easier:
coll.CollectionCountChanged.Select(x => x > 0);

Related

How to unwrap nested IObservable aka IObservable<IObservable<T>>

I am trying to create an IObservable<string> from the following code, but i can't seem to find a way of how to properly unwrap the value of the event handler.
What is happening is that the PasswordBox might change, so whenever it does i want to observe on that, and provide a string resource whenever the password changed event is raised. It works fine if i do it with ordinary events but i am curious on how this would work using System.Reactive.
var passwordChanged = WhenPropertyChanged
.Where(name => nameof(PasswordBox) == name)
.Select(d => PasswordBox)
.Where(d => d != null)
.Select(box =>
{
return Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
handler => box.PasswordChanged += handler,
handler => box.PasswordChanged -= handler);
}).Select(d => nameof(Password));
to me it seems like there has to be some way within the Select(box => ... part where i can return a different object (other than IObservable<IObservable<RoutedEventArgs>>), which can be used to subscribe to properly.
Doing it like the following works. But i think reactive does a better job of avoiding event handler memory leaks if you use it end to end.
var passwordHasChanged = new Subject<string>();
// listen for changes of the password
WhenPropertyChanged
.Where(name => nameof(PasswordBox) == name)
.Select(d => PasswordBox)
.Where(d => d != null)
.Subscribe(box =>
{
box.PasswordChanged += (sender, args) => passwordHasChanged.OnNext(nameof(Password));
});
passwordHasChanged.Subscribe(d => Log.Debug("Password changed"));
Avoid using Subjects wherever possible. Subjects are like the mutable variables of Rx, they don't compose and read imperatively rather than declaratively.
If you want events from only the last password input, use Switch.
Switch works on an IObservable<IObservable<T>>, and unsubscribes from the previous observable when it gets a newer observable.
var passwordChanged = WhenPropertyChanged
.Where(name => nameof(PasswordBox) == name)
.Select(d => PasswordBox)
.Where(d => d != null)
.Select(box =>
Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
handler => box.PasswordChanged += handler,
handler => box.PasswordChanged -= handler);
)
.Switch()
.Select(d => nameof(Password));

How can I merge observables once an item in the first observable satisfies a predicate?

I want to take two observables, and emit from the first observable until one of the items meets a predicate, and then start emitting events merged from both, like this:
letters: -A----B---C----d---------e-----f------g--
numbers: ---1-------2----------3-----4-----5------
predicate: IsLowerCase()
result: -A----B---C----d12----3--e--4--f--5---g--
How would it be possible to do this in C# using System.Reactive?
Thanks
Switch is almost always the key:
var letters = new Subject<string>();
var numbers = new Subject<string>();
var predicate = lettersPublished.Select(s => s.All(c => char.IsLower(c)));
var numbersCached = numbers.Replay().RefCount();
var dummySubscription = numbersCached.Subscribe();
var combined = lettersPublished.Merge(numbersCached);
var switchFlag = predicate.Where(b => b).Take(1);
var result = switchFlag.StartWith(false).Select(b => b ? combined : letters).Switch();
var resultWithDisposal = Observable.Using(() => dummySubscription, _ => result);
Construct the two observables, one before the predicate is true (letters in your case), and one after (combined), then use an observable to switch between them (switchflag). The dummySubscription is necessary to get Replay to cache all values prior to the latter subscription. Using is used to dump the dummySubscription.
You can also do this all in a single expression (given letters and numbers) as follows:
var oneLiner = letters.Publish(_letters => numbers.Replay(_numbers =>
_letters
.Select(s => s.All(c => char.IsLower(c)))
.Where(b => b)
.Take(1)
.StartWith(false)
.Select(b => b ? _letters.Merge(_numbers) : letters)
.Switch()
));

ReactiveUI/Reactive Extensions: how to clear ObservableAsPropertyHelper

I am using the ReactiveUI framework to do searching for a list of airports in the world.
I have setup the ObservableAsPropertyHelper that is the output of the suggested airports from a SearchTerm property in the ViewModel. The following is the definition for the ObservableAsPropertyHelper. In the view, I have a listbox that binds to this property. I would like to be able to clear the listbox explicitly(because once the user selected a suggested item, I want to populate the SearchTerm with the selected airport and clear the suggested list). Is there an elegant way to implement this?
var searchTerms = this.ObservableForProperty(x => x.SearchTerms).Where(x => canSearch).Value().Throttle(TimeSpan.FromMilliseconds(500));
var searchResults = searchTerms.SelectMany(SearchAirports);
var latestResults = searchTerms.CombineLatest(searchResults, (s, r) => r.SearchTerm != s ? null : r.AirportLiteWithWeights).Where(x => x != null);
_airportLiteWithWeights = latestResults.ToProperty(this, x => x.AirportLiteWithWeights);
Here's how I would do it - it's a bit tricky because the actual sequence of events feeds back into itself (i.e. selecting an item sets SearchTerms)
// The act of selecting an item is very ICommand'y, let's model it
// as such.
ReactiveCommand SuggestionItemSelected = new ReactiveCommand();
// Important to *not* signal a change here by touching the field
// so that we avoid a circular event chain
SuggestionItemSelected.Subscribe(x => _searchTerms = x);
// NB: Always provide a scheduler to Throttle!
var searchTerms = this.WhenAnyValue(x => x.SearchTerms)
.Where(x => canSearch)
.Throttle(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler);
// Select + Switch will do what you were trying to do with CombineLatest
var latestResults = searchTerms
.Select(SearchAirports);
.Switch()
// The listbox is the combination of *Either* the latest results, or the
// empty list if someone chooses an item
var latestResultsOrCleared = Observable.Merge(
latestResults,
SuggestionItemSelected.Select(_ => new List<Results>()));
latestResultsOrCleared
.ToProperty(this, x => x.AirportLiteWithWeights, out _airportLiteWithWeights);

Linq, IQueryable get most frequent element object

My main problem lies in understanding IGrouping<int, SubForm>. I want the most frequent object (MyClass). This is the code I have now:
var subForm =
classes.GroupBy(c => c.SubFormId)
.OrderByDescending(sf => sf.Count())
.FirstOrDefault();
This returns IGrouping<int, SubForm>. To get the actual object, I have to do another FirstOrDefault() but the compiler shows that there might be a null exception.
This is the code to get the actual subform, can it be done better?
var subForm =
classes.GroupBy(c => c.SubFormId)
.OrderByDescending(sf => sf.Count())
.FirstOrDefault().FirstOrDefault().SubForm;
You could just check to make sure there is at least one item in your classes collection (so it is guaranteed there is at least one group) either at the end or the beginning:
if(classes.Any())
{
var subForm = classes.GroupBy(c => c.SubFormId)
.OrderByDescending(sf => sf.Count())
.First().First().SubForm;
}
Or:
var topGroup = classes.GroupBy(c => c.SubFormId)
.OrderByDescending(sf => sf.Count())
.FirstOrDefault();
if(topGroup!=null)
subForm = item.First().SubForm;
I think you want:
var sfGroup = classes.GroupBy(c => c.SubFormId)
.OrderByDescending(sf => sf.Count())
.FirstOrDefault();
int count = sfGroup.Count();
MyClass subForm = sfGroup.FirstOrDefault();
you could just use First. It would throw an exception if nothing is found.
var subForm = classes.GroupBy(c => c.SubFormId)
.OrderByDescending(sf => sf.Count())
.FirstOrDefault();
return subForm == null ? default(SubForm) : subForm.Select(s => s.SubForm);
You are missing a select:
var subForm = (
from c in classes
group c by c.SubFormId into g
select g.Key)
.FirstOrDefault();

Adding an observable sequence after subscription

We are using Rx to monitor activity within our silverlight application so that we can display a message to the user after a period of inactivity.
We are turning events (mouse moves etc.) into observables and then merging the observables together to create a single (allActivity) observable. We then throttle the allActivity observable using a timespan and something subscribes to be notified when the system has been inactive for a period of time.
How can I add a new observable/ sequence to this after the subscription (so that the subscription picks this up without unsubscribing and resubscribing).
e.g. merge several sequences together, throttle, subscribe. Now add an additional sequence to the observable that has been subscribed to.
Example code:
private IObservable<DateTime> allActivity;
public void CreateActivityObservables(UIElement uiElement)
{
// Create IObservables of event types we are interested in and project them as DateTimes
// These are our observables sequences that can push data to subscribers/ observers
// NB: These are like IQueryables in the sense that they do not iterate over the sequence just provide an IObservable type
var mouseMoveActivity = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(h => uiElement.MouseMove += h, h => uiElement.MouseMove -= h)
.Select(o => DateTime.Now);
var mouseLeftButtonActivity = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(h => uiElement.MouseLeftButtonDown += h, h => uiElement.MouseLeftButtonDown -= h)
.Select(o => DateTime.Now);
var mouseRightButtonActivity = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(h => uiElement.MouseRightButtonDown += h, h => uiElement.MouseRightButtonDown -= h)
.Select(o => DateTime.Now);
var mouseWheelActivity = Observable.FromEventPattern<MouseWheelEventHandler, MouseWheelEventArgs>(h => uiElement.MouseWheel += h, h => uiElement.MouseWheel -= h)
.Select(o => DateTime.Now);
var keyboardActivity = Observable.FromEventPattern<KeyEventHandler, KeyEventArgs>(h => uiElement.KeyDown += h, h => uiElement.KeyDown -= h)
.Select(o => DateTime.Now);
var streetViewContainer = HtmlPage.Document.GetElementById("streetViewContainer");
var mouseMoveHandler = new EventHandler<HtmlEventArgs>(this.Moo);
bool b = streetViewContainer.AttachEvent("mousemove", mouseMoveHandler);
var browserActivity = Observable.FromEventPattern<Landmark.QDesk.ApplicationServices.IdleTimeoutService.MouseMoveHandler, HtmlEventArgs>(h => this.MyMouseMove += h, h => this.MyMouseMove -= h).Select(o => DateTime.Now);
// Merge the IObservables<DateTime> together into one stream/ sequence
this.allActivity = mouseMoveActivity.Merge(mouseLeftButtonActivity)
.Merge(mouseRightButtonActivity)
.Merge(mouseWheelActivity)
.Merge(keyboardActivity)
.Merge(browserActivity);
}
public IDisposable Subscribe(TimeSpan timeSpan, Action<DateTime> timeoutAction)
{
IObservable<DateTime> timeoutNotification = this.allActivity.Merge (IdleTimeoutService.GetDateTimeNowObservable())
.Throttle(timeSpan)
.ObserveOn(Scheduler.ThreadPool);
return timeoutNotification.Subscribe(timeoutAction);
}
There's an overload to Merge that takes in an IObservable<IObservable<TSource>>. Make the outer sequence a Subject<IObservable<TSource>> and call OnNext to it when you want to add another source to the bunch. The Merge operator will receive the source and subscribe to it:
var xss = new Subject<IObservable<int>>();
xss.Merge().Subscribe(x => Console.WriteLine(x));
xss.OnNext(Observable.Interval(TimeSpan.FromSeconds(1.0)).Select(x => 23 + 8 * (int)x));
xss.OnNext(Observable.Interval(TimeSpan.FromSeconds(0.8)).Select(x => 17 + 3 * (int)x));
xss.OnNext(Observable.Interval(TimeSpan.FromSeconds(1.3)).Select(x => 31 + 2 * (int)x));
...
The easiest way to do this would be to use an intermediate subject in place of the Merge calls.
Subject<DateTime> allActivities = new Subject<DateTime>();
var activitySubscriptions = new CompositeDisposable();
activitySubscriptions.Add(mouseMoveActivity.Subscribe(allActivities));
activitySubscriptions.Add(mouseLeftButtonActivity.Subscribe(allActivities));
//etc ...
//subscribe to activities
allActivities.Throttle(timeSpan)
.Subscribe(timeoutAction);
//later add another
activitySubscriptions.Add(newActivity.Subscribe(allActivities));
The Subject class will stop passing OnNext (and further OnError and OnCompleted) events from any of the observables it is subscribed to if it receives any OnError or OnCompleted.
The main difference between this approach and your sample is that it subscribes to all the events when the subject is created, rather than when you subscribe to the merged observable. Since all of the observables in your example are hot, the difference should not be noticeable.

Categories

Resources