WhenAny and ToProperty - c#

I think I am missing a fundamental Observable concept.
I have a ChargeViewModel class which has a ReactiveList property which is a collection of itself
ChargeViewModel.Charges which contains related charges
I want to observe the latest entry in the charges collection (it has a ChargeViewModel.lastModified) and also have other properties in the UI based on the latest entry.
In the ctor I have this code which works to initialize the values; however it does not update the values for latestActionDate, latestBillToName, lastestNote when the observable variable "last" changes
I want the UI to update if the user updates the "last" charge or creates a new "last".
var last = Charges?
.Changed
.Select(_ => Charges.OrderByDescending(c => c.Model.LastModified).FirstOrDefault())
.StartWith(Charges.OrderByDescending(c =>c.Model.LastModified).FirstOrDefault());
last
.Select(c => c.Model.LastModified)
.ToProperty(this, vm => vm.LatestActionDate, out latestActionDate);
last
.Select(c => c.Model.BillToName)
.ToProperty(this, vm => vm.LatestBillToName, out latestBillToName);
last
.Select(c => c.Model.Note)
.ToProperty(this, vm => vm.LatestNote, out latestNote);

ReactiveList (by default) only notifies upon elements added/moved/removed, not upon content changes. It's fairly simply to enable it (and maybe you already did) by setting ChangeTrackingEnabled=true.
However when enabled, you'll receive such motifications on ItemChanged observable, and not on Changed one, so in your case you probably need to do:
Charges.Changed.Select(_ => Unit.Default).Merge(Charges.ItemChanged.Select(_ => Unit.Default))
.Select(_ => /* ... */)
....

Related

Update ItemsControl.ItemsSource based on selected object of a another ItemsControl

We use ReactiveUI and DynamicData and have two ListBoxes: ListBoxA and ListBoxB. Based on the selection of ListBoxA, the list in ListBoxB should be updated (Not filtered). Seems straightforward but I am having some trouble refreshing ListBoxB
The ListBoxes are bound like so in the View:
this.OneWayBind(ViewModel, vm => vm.ItemsA, v => v.ListBoxA.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedItemA, v => v.ListBoxA.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ItemsB, v => v.ListBoxB.ItemsSource).DisposeWith(disposables);
ViewModel:=
_storage.PoolA.Connect()
.Transform(m => new ViewModelForA(m))
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _itemsA)
.Subscribe();
SelectedItemA.PoolB.Connect()
.Transform(c => new ViewModelForB(c))
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _itemsB)
.Subscribe();
Any help would be much appreciated!
Every time you select something in ListBoxA your SelectedItemA object changes and you need to recreate you connection to it.
At first we create an IObservable from SelectedItemA and then use Switch operator from DynamicData
class MyViewModel: INotifyPropertyChange
{
public MyViewModel()
{
_storage.PoolA.Connect()
...
this.WhenPropertyChanged(x => x.SelectedItemA)
.Select(x => x.Value.PoolB)
.Switch() // Switch from DynamicData
.Transform(c => new ViewModelForB(c))
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _itemsB)
.Subscribe();
}
}

How do i sort my events with regards to "grandchildren's" date

We’ve got an EF6 MVC3 codeFirst-site. Our Events may have a collection of EventRallies. And the EventRallies may have a collection of EventOccasions
I want to order them so that events with any future occasions is at the top, followed by events where all occasions are in the past, and then events that doesn´t have any rallies or occasinons tied to them.
The following attempt results in an :System.ArgumentException: DbSortClause expressions must have a type that is order comparable- error ;)
return context.Events.Where(x => x.OrganizationId == id).
Include(x => x.EventRallies).
Include(x => x.EventRallies.Select(e => e.EventOccasions)).
OrderBy(x => x.EventRallies.OrderByDescending(d=>d.EventOccasions.FirstOrDefault().Date)).ToList();
Any suggestions?
You need SelectMany to get one list of all EventOccasions of one Event. Select their dates. This list can be ordered by date (descending). The first date in this list is the ordering key for the Events.
If you order by this date key descending, the future events will come first, then the past events, followed by the events without any occasion.
context.Events.Where(x => x.OrganizationId == id)
.Include(x => x.EventRallies)
.Include(x => x.EventRallies.Select(e => e.EventOccasions))
.OrderByDescending(x => x.EventRallies.SelectMany(d => d.EventOccasions)
.Select(occ => occ.Date)
.OrderByDescending(d => d)
.FirstOrDefault()).ToList();

c# Observable subject hot and cold

I often have the situation where I want a UI element to "watch" an underlying value - supposing I am displaying an int - I want an an IObservable that I can subscribe to.
I have been using a Subject underneath, so I can just set it. That works really well... except for the first time. If I setup the subject - then later subscribe to it by opening a new UI element - it doesn't trigger an onnext until the next change.
I basically want something that works like a subject - but always and immediately does an onNext of the latest value to any new subscribers.
I know I can write such a construct myself - but it seems a common use case - is there something standard I'm missing?
You want either BehaviorSubject<T> or ReplaySubject<T>.
BehaviorSubject<T> replays the single most recent value and requires that you give an initial value in the constructor.
You use it like this:
var bs = new BehaviorSubject<int>(0);
bs.Subscribe(x => Console.WriteLine(x));
bs.OnNext(42);
bs.Subscribe(x => Console.WriteLine(x));
That would output 0, then 42, then 42 to the console.
ReplaySubject<T> is a little more general. It allows you to specify the number of values to replay, but doesn't enforce that you provide an initial value.
var rs = new ReplaySubject<int>(1);
rs.Subscribe(x => Console.WriteLine(x));
rs.OnNext(42);
rs.Subscribe(x => Console.WriteLine(x));
This produces 42, and then 42 to the console.
Compare this to a standard Subject<T>.
var s = new Subject<int>();
s.Subscribe(x => Console.WriteLine(x));
s.OnNext(42);
s.Subscribe(x => Console.WriteLine(x));
That just produces 42.
If you are using Rx in UI, you should take a look at ReactiveUI.
It has handy extension .ToProperty and more:
_isValid = this.WhenAnyValue(x => x.Username, x => x.Password, (user, password) => !string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(password)).ToProperty(this, x => x.IsValid);
public bool IsValid => _isValid.Value;
This is basically what you are doing:
this.WhenAnyValue(x => x.Property) => property as observable
obs.ToProperty() => observable to property that you can bind to UI
both work with INotifyPropertyChanged

Using reactive extensions how I can create a dynamic list with values who can expire

Scenario
I'm receiving differents notification ids every 100 ms (Source1) and I need to do put every id in a Cache with the specific received date, if the id came twice I only update the date. After that I need to search information for the ids invoking a service, when I receive that information on my app, I need to show it ordered by the received date, updating the screen every 5 seconds. If any id is not refreshed in the range of 10 seconds by the Source1, it needs to change of state to display it in a different category or state
Problem
I'm trying to use Reactive Extensions to solve this problem, but I'm not sure if it's the correct technology because:
I don't know where I should have the cache and how to manage those states
How is the best way to manage the concurrency in general to invoke the external service in the meantime I can receive more ids could be new or old
At the end to have clean list as a result of information where I can see which elements are being updated and which of them not.
Can anyone help me? Thanks
It sounds like the .Scan operator might meet your needs.
Try this:
var source = new Subject<int>();
var query =
source
.Scan(new Dictionary<int, DateTime>(), (a, x) =>
{
a[x] = DateTime.Now;
return new Dictionary<int, DateTime>(a);
})
.Select(x => x.OrderByDescending(y => y.Value));
You can test this with the following code:
var values = new [] { 1, 2, 1, 3, 2, 1 };
Observable
.Interval(TimeSpan.FromSeconds(5.0))
.Take(values.Length)
.Select(x => values[x])
.Subscribe(source);
I get:
It's better though to use ImmutableDictionary so then the query looks like this:
var query =
source
.Scan(
new Dictionary<int, DateTime>().ToImmutableDictionary(),
(a, x) => a.SetItem(x, DateTime.Now))
.Select(x => x.OrderByDescending(y => y.Value));
var query =
source
.Scan(ImmutableDictionary<int, DateTime>.Empty, (a, x) => a.SetItem(x, DateTime.Now))
.Select(x => Observable.Interval(TimeSpan.FromSeconds(5.0)).Select(y => x).StartWith(x))
.Switch()
.Select(x => x.OrderByDescending(y => y.Value));
Try this query - it continues to produce values when your source does, but every 5 seconds after the latest value to come out it repeats the last item (unless the source produces a value and it then reset the 5 second timer).

Time-varying values in Rx

I have two observables. One is from Observable.fromEvent(..), where the underlying event is the user checking a Winforms checkbox. The other is Observable.Interval(..) which I subscribe to in order to do some IO, and I would like to prevent this observable from doing IO, whenever the checkbox is not checked.
I could do it like this:
var gui = new GUI();
var booleans = Observable
.FromEvent<GUI.NewAllowHandler, bool>(
h => gui.NewAllow += h,
h => gui.NewAllow -= h)
Observable.Interval(TimeSpan.FromSeconds(10))
.CombineLatest(booleans, Tuple.Create)
.Where(t => t.Item2)
.Select(t => t.Item1)
.Subscribe(l => DoStuff(l));
but this has the overhead of mixing the booleans in and out of the stream. A nicer way of doing this would be, if I could generate a time-varying value from the booleans variable, which at all times had the value of the last event. Then I could do something like this:
var gui = new GUI();
var booleanState = Observable // typeof(booleanState) == ???
.FromEvent<GUI.NewAllowHandler, bool>(
h => gui.NewAllow += h,
h => gui.NewAllow -= h)
.TimeValue() // hypothetical syntax
Observable.Interval(TimeSpan.FromSeconds(10))
.Where(_ => booleanState)
.Subscribe(l => DoStuff(l));
, which to me seems much closer to the problem statement. Is there anything like this in Rx, or is there anything else, that could make such problems easier to handle?
The Where statement in your interval should work with a properly scoped normal bool:
var booleans = Observable
.FromEvent<GUI.NewAllowHandler, bool>(
h => gui.NewAllow += h,
h => gui.NewAllow -= h)
var isBoxChecked = false;
booleans.Subscribe(t => isBoxChecked = t);
Observable.Interval(TimeSpan.FromSeconds(10))
.Where(_ => isBoxChecked)
.Subscribe(l => DoStuff(l))
Edit: Per your comment, another way of doing it:
intervals = Observable.Interval(TimeSpan.FromSeconds(10));
booleans
.Where(t => t)
.SelectMany(_ => intervals.TakeUntil(booleans))
.Subscribe(l => DoStuff(l))
You need to model the checkbox checked state as Behavior and not as Event stream (because behavior has always a value and this value changes over a period of time - which fits with checkbox checked state). So you can do something like:
var booleans = new BehaviorSubject<bool>(chk.Checked)
var chkEvents = ... //generate boolean observable from checkbox check event
chkEvents.Subscribe(booleans);
Observable.Interval(TimeSpan.FromSeconds(10))
.Where(i => booleans.First())
.Subscribe(i => DoIO());
I'm going to give you two solutions. The first is a very simple and hopefully obvious one using only one observable. The second is a uses both observables.
Since you want to allow the IO only when the box is checked then this is the simplest approach:
Observable
.Interval(TimeSpan.FromSeconds(10))
.Where(_ => gui.IsChecked)
.Subscribe(l => DoStuff(l));
No need at all for the other observable.
But if you really need to use it then the Switch() extension method is your best bet. Try this:
booleans
.Select(b => b == true
? Observable.Interval(TimeSpan.FromSeconds(10))
: Observable.Empty<long>())
.Switch()
.Subscribe(l => DoStuff(l));
It's pretty clean and helps to show that there are empty periods if the checkbox is not ticked.
I hope this helps.

Categories