How to Marshal to UI thread correctly in ReactiveUI 6.0 - reactiveui

I have an Observable like this:
var posChangeObs =
Observable.Publish<DisplayPositionModel>(
Observable
.FromEventPattern<EventHandler<PositionEventArgs>, PositionEventArgs>(h => cine.PositionChange += h,
h => cine.PositionChange -= h,
RxApp.MainThreadScheduler)
.Select(x => new DisplayPositionModel(cine.ToDisplayPosition(x.EventArgs.Position), x.EventArgs.LinearFrameIndex)),
new DisplayPositionModel(cine.ToDisplayPosition(cine.CurrentCinePosition), cine.CurrentLinearCinePosition));
The event this tracks will always occur on a different thread. I pass this Observable to a lot of different view models. In some view models the eventArgs are set to a property using ToProperty. In others I just Subscribe and DoStuff(TM).
What I want to know is how to ensure that these are always marshaled to the UI thread. I have tried adding ObserveOn(RxApp.Main...) on all of the ToProperty and Subscribe calls, but that did not work.
Here is an example of how I am using ToProperty right now and getting cross thread exception:
posChangeObs.ToProperty(this, x => x.CurrentPosition, out _CurrentPosition);
and here is an example Subscription:
posChangeObs
.Select(x => x.LinearFrameIndex)
.Subscribe(x => this.CurrentLinearFrameIndex = x,
e =>
{
throw e;
});

The answer was to add the ObserveOn(RxApp.MainThreadScheduler) to the return of FromEventPattern. Like this
var posChangeObs = Observable.Publish<Position>( Observable.FromEventPattern<EventHandler<PositionEventArgs>,PositionEventArgs>(h => x.PositionChange += h, h => x.PositionChange -= h)
.ObserveOn(RxApp.MainThreadScheduler)
.Select(x => ...));

Related

RxJS merge observables but prefer one over the other

I have two observables A and B, and I'd like to create an observable which emits the latest value from B, but if B hasn't emitted anything yet, it emits the latest value from A. I was thinking something like this:
merge(A.takeUntil(B), B)
But this seems a little counterintuitive, and I'd like to have a more readable approach. Is there a more canonical Rx way to do this?
Custom Operator
This solution is very readable in use but complicated by the fact that you hide the complexity within a custom operator. The benefit of a custom operator is that you only subscribe to each source observable once. This means your observables don't have to be "hot".
Unfortunately, this operator breaks synchronously executing observables and has each value pass through the event loop.
function prefer<T, R>(...observables: Observable<R>[]): Observable<R>{
return new Observable(observer => {
const subscrptions = new Array<Subscription>();
const unsub = (index) => {
for(let i = index; i < subscrptions.length; i++){
subscrptions[i].unsubscribe();
}
}
observables
.map(stream => publish()(stream))
.forEach((stream, index) => {
subscrptions.push(stream.subscribe((payload: R) => {
observer.next(payload);
unsub(index + 1);
subscrptions.length = index + 1;
}));
stream.connect();
});
return { unsubscribe: () => unsub(0) }
})
}
Operator in Use
prefer(
interval(10000).pipe(map(_ => "Every 10,000")),
interval(5000).pipe(map(_ => "Every 5,000")),
interval(1000).pipe(map(_ => "Every 1,000")),
interval(250).pipe(map(_ => "Every 250"))
).subscribe(console.log);

How to Observe Properties of Objects within an ReactiveList

I have the following Problem. I have got
aProductionOrderList = new ReactiveList<ProductionOrderViewModel>();
the ProductionOrderViewModel has a Property Itemsleft, which gets updated internally
private readonly ObservableAsPropertyHelper<int> itemsLeft;
public int ItemsLeft => this.itemsLeft.Value;
...
this.itemsLeft = this
.WhenAny(x => x.Ticks, x => x.Value)
.ToProperty(this, x => x.ItemsLeft, scheduler: DispatcherScheduler.Current);
What i want to accomplish is when ever any item in the List comes to a point where the Itemsleft property is 0, it should be removed from the List.
I tried it this way
ProductionOrderList.ItemChanged.Where(x => x.Sender.ProductionOrder.ItemsLeft ==0)
.Subscribe(v =>
{
// do stuff
});
but it did not work unfortunately.
Help is very much appreciated.
So I found a working solution, it even seems rigth and clean, though I am totally open for improvements. I have done the following
this.WhenAnyObservable(o => o.ProductionOrderList.ItemChanged)
.Where(x => x.PropertyName == "ItemsLeft")
.Select(x => x.Sender)
.Where(x => x.ItemsLeft == 0)
.Subscribe(x =>
{
ProductionOrderList.Remove(x);
});
I hope this helps others which habe a similar problem.

How to delay ReactiveCommand.CreateFromTask first execution

I have an App made with Xamarin.Forms and ReactiveUI.
Picture a View in this App where you have a kind of dropdown (actually a button that push another view where the user can filter and select one option) and when this "dropdown" is changed, I need to reload a list based on its value.
This "dropdown" won't start with some value, I need to make a async request, grab a value and then update the view.
The problem is, when I'm creating the command to load the documents:
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll, m_scheduler);
I need the Id from the SelectedToDoList, but this will be null at this point.
There's any way to delay this first execution of a command? Or maybe there's a better workflow to solve this?
Here is a sniped on how I'm doing it right now. Let me know if more information is needed.
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll, m_scheduler);
ChangeToDoListCommand = ReactiveCommand.CreateFromTask<DocumentListViewModel, bool>(vm => this.PushPageFromCacheAsync<ToDoListViewModel>((model) => model.ParentViewModel = this));
this.WhenActivated((CompositeDisposable disposables) =>
{
SelectedItem = null;
var SelectedTodoListChanged =
this
.WhenAnyValue(x => x.SelectedTodoList)
.Throttle(TimeSpan.FromSeconds(1), RxApp.MainThreadScheduler)
.Publish();
SelectedTodoListChanged
.Where(x => x == null)
.Subscribe(async _ => SelectedTodoList = await viewService.GetMyToDoListByVaultAsync(RuntimeContext.Current.VaultId))
.DisposeWith(disposables);
SelectedTodoListChanged
.Where(x => x != null)
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
SelectedTodoListChanged.Connect();
LoadAllDocuments
.ObserveOn(m_scheduler)
.SubscribeOn(m_scheduler)
.Subscribe(list => AddViewsToList(list.ToList()))
.DisposeWith(disposables);
If I understand your question correctly, you need to ensure Id is not null before calling InvokeCommand:
SelectedTodoListChanged
.Where(x => x?.Id != null)
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
Perhaps a better option is to bake this knowledge into the command itself. Since InvokeCommand respects the execution window of the command (as of RxUI 7, that is), if your command's CanExecute is currently false then InvokeCommand will not actually invoke your command:
var canLoadAllDocuments = this
.WhenAnyValue(x => x.SelectedTodoList?.Id)
.Select(id => id != null);
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(
_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll,
canLoadAllDocuments,
m_scheduler);
Now you can do this:
SelectedTodoListChanged
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);

How to create Hot observable in reactive extension

I am trying to use Reactive extensions with Oracle AQ. When a message comes on Oracle Queue, it fires a "OracleAQMessageAvailableEvent" which tells the consumer that there is a message. Inside the OracleAQMessageAvailableEventHandler the consumer calls OracleAQQueue.Dequeue() to retrieve the message.
I have got the above working with RX. Following is the code I have used.
var messages = Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
h => _queue.MessageAvailable += h, h => _queue.MessageAvailable -= h)
.Where(x => x.EventArgs.AvailableMessages > 0)
.Select(x =>
{
OracleAQMessage msg = _queue.Dequeue();
return (UpdateMsg) msg.Payload;
});
messages.subscribe(....)
Problem is that if I subscribe to messages once everything works but if I subscribe to messages multiple times (i.e multiple consumers inside my application) then every consumer will try to call "_queue.Dequeue()" and every call after the first call will fail if we have no new message.
Could anyone please guide me what should I do. I think, my scenario is for Hot Observable but I am struggling to get my head around it.
I think you are correct that you are looking for a Hot Observable.
If we follow the code it may be more clear why you are seeing _queue.Dequeue(); being called multiple times.
First you subscribe to the event from Oracle
Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
h => _queue.MessageAvailable += h,
h => _queue.MessageAvailable -= h)
This is just like hooking up an event handler as you would have in a pre-Rx world.
Everyone the listens (subscribes) will receive the same events.
If they subscribe after an event is raised, then they have missed it.
Then you filter out empty sets.
.Where(x => x.EventArgs.AvailableMessages > 0)
Nothing special there.
Then you perform a side effect from inside your query.
.Select(x =>
{
OracleAQMessage msg = _queue.Dequeue();
return (UpdateMsg) msg.Payload;
});
The side effect here is that you are making a destructive read (Dequeue).
All subscribers when they are pushed an event from the upstream _queue.MessageAvailable will all try to call Dequeue().
To avoid all of the subscribers to invoke the side effect, you can make the sequence Hot (as you suggested).
To do this you can look at the Publish() operator.
The Publish() operator will return you an IConnectableObservable<T> that just extends IObservable<T> by adding the Connect() method.
This allow fine grained control of when the subscription logic is executed.
However, this maybe too much control for you, and you will probably find that RefCount() to be just what you need.
Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
h => _queue.MessageAvailable += h,
h => _queue.MessageAvailable -= h)
.Where(x => x.EventArgs.AvailableMessages > 0)
.Select(x =>
{
OracleAQMessage msg = _queue.Dequeue();
return (UpdateMsg) msg.Payload;
})
.Publish()
.Refcount();
Now each of your subscribers will receive the same message, and your Dequeue() side effect will only be invoked once per event (and only while there are subscribers).
Hot and Cold observable's are covered here
Lee Campbell, Sorry my bad. The solution you mentioned does work. Actually, I was using it incorrectly. I have a class call QueueWrapper which has a property called Messages. I had this implementation of Messages
public IObservable<UpdateMsg> Messages {
get { return Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
h => _queue.MessageAvailable += h,
h => _queue.MessageAvailable -= h)
.Where(x => x.EventArgs.AvailableMessages > 0)
.Select(x =>
{
OracleAQMessage msg = _queue.Dequeue();
return (UpdateMsg) msg.Payload;
})
.Publish()
.Refcount();
}}
and my client code was subscribing using Messages property like this
// First Subscription
_queueWrapper.Messages.Subscribe(....)
// Second Subscription
_queueWrapper.Messages.Subscribe(....)
so for each subscription, Messages property was returning a new IObservable. To fix this, I moved the initialization of observable to the constructor of QueueWrapper i.e following code :
public QueueWrapper() {
_messages = Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
h => _queue.MessageAvailable += h,
h => _queue.MessageAvailable -= h)
.Where(x => x.EventArgs.AvailableMessages > 0)
.Select(x =>
{
OracleAQMessage msg = _queue.Dequeue();
return (UpdateMsg) msg.Payload;
})
.Publish()
.Refcount();
}
and my Messages property just return _messages;
public IObservable<UpdateMsg> Messages { get { return _messages; } }
After that everything started working as expected.

Multiple Subscriptions on one observable

I have a read-write property on my ViewModel and need two separate actions to occur when it changes :
public decimal Paid {
get { return paid; }
set { this.RaiseAndSetIfChanged(ref paid, value); }
}
...
in the ctor:
this.WhenAnyValue(pb => pb.Paid)
.Select(amount => NumberToEnglish.ToSentence(amount))
.ToProperty(this, x => x.AmountInWords, out amountInWords);
this.WhenAnyValue(pb => pb.Paid)
.Subscribe(amount => SelectedPaymentBatch.Paid = amount );
Is there a way to do this in one statement or is this the correct way to do this?
It's very much feasible to do both in one stream, e.g using Do operator (see below), but I would recommend to keep your current approach, as it correctly separates both concerns, which are unrelated but the fact they trigger on the same property (but that could change).
this.WhenAnyValue(pb => pb.Paid)
.Do(amount => SelectedPaymentBatch.Paid = amount)
.Select(amount => NumberToEnglish.ToSentence(amount))
.ToProperty(this, x => x.AmountInWords, out amountInWords);

Resources