Reputation: 798
In my app I have a ViewModel with two lists. There is also an action that CanExecute property depends on both list states. I'm using ReactiveUI. So far I got following (working) implementation:
public ReactiveList<Defect> Defects { get; } = new ReactiveList<Defect>();
public ReactiveList<State> States { get; } = new ReactiveList<State>();
public ReactiveCommand<object> Print { get; }
// this stream is artificial it is only needed to get notifications from both above
private IObservable<bool> SelectingStream { get; }
ctor()
{
Defects.ChangeTrackingEnabled = true;
States.ChangeTrackingEnabled = true;
SelectingStream = States.ItemChanged.CombineLatest(Defects.ItemChanged, (a, b) =>
{
// here is the condition that needs to be met in order to can execute action
return States.Count(s => s.IsSelected) == 1 &&
Defects.Any(d => d.IsActive);
});
Print = ReactiveCommand.Create(
this.WhenAnyObservable(x => x.SelectingStream)
);
}
It does work, however I think this approach is more like workaround. Is it OK ore there is more straightforward solution?
Upvotes: 2
Views: 362
Reputation: 2962
As you probably need to listen/react to inner object changes, ItemChanged
is probably the simplest it can get (albeit pretty heavy).
Some remarks though:
You can avoid the observable property and the whenAnyObservable
call and simply write Print = ReactiveCommand.Create(selectingStream)
Beware that ItemChanged
will not trigger when you add/remove elements to the list, so your observable condition should also be merged with the lists Changed
streams to cover for that
you should replace CombineLatest
by Merge
to avoid leaking objects (you don't really need to store the latest state of each stream, a/b aren't used).
you could avoid triggering the check when properties other than the ones you're relying upon are changed
Ultimately the result should look like:
new [] {
States.Changed.SelectUnit(),
States.ItemChanged.Where(ea => ea.PropertyName == "IsSelected").SelectUnit(),
Defects.Changed.SelectUnit(),
Defects.ItemChanged.Where(ea => ea.PropertyName == "IsActive").SelectUnit() }
.Merge()
.Select(_ => States.Count(s => s.IsSelected) == 1 && Defects.Any(d => d.IsActive))
(using a handy SelectUnit()
extension method)
Not really simpler, is it ? :)
Upvotes: 4