I have 2 DecimalUpDown controls, num_one and num_two, bound to properties First and Second respectively. When First is changed it will contact a server to calculate the value of Second, and vice-versa. Firing the server calls asynchronously freed the UI but, upon quick firing (scroll wheel for example), the last request isn’t always the last to return so the values may become out of sync.
Using Reactive I’m trying to Throttle the calls to only fire the server call after the user has stopped making changes for a little while. The problem is that when you make a change during an update, the Properties changing start triggering each other and get stuck in back and forth depending on the TimeSpan of the Throttle.
public MainWindow()
{
InitializeComponent();
DataContext = this;
Observable.FromEventPattern<RoutedPropertyChangedEventHandler<object>, RoutedPropertyChangedEventArgs<object>>(h => num_one.ValueChanged += h, h => num_one.ValueChanged -= h)
.Throttle(TimeSpan.FromMilliseconds(100), Scheduler.ThreadPool)
.Subscribe(x =>
{
Thread.Sleep(300); // simulate work
Second = (decimal)x.EventArgs.NewValue / 3.0m;
});
Observable.FromEventPattern<RoutedPropertyChangedEventHandler<object>, RoutedPropertyChangedEventArgs<object>>(h => num_two.ValueChanged += h, h => num_two.ValueChanged -= h)
.Throttle(TimeSpan.FromMilliseconds(100), Scheduler.ThreadPool)
.Subscribe(x =>
{
Thread.Sleep(300); // simulate work
First = (decimal)x.EventArgs.NewValue * 3.0m;
});
}
private decimal first;
public decimal First
{
get { return first; }
set
{
first = value;
NotifyPropertyChanged("First");
}
}
private decimal second;
public decimal Second
{
get { return second; }
set
{
second = value;
NotifyPropertyChanged("Second");
}
}
There’s an inbuilt Rx operator that can help you do exactly what you want without using
Throttleand timeouts – it’s theSwitchoperator.The
Switchoperator doesn’t work onIObservable<T>so most times you would never see it in intellisense.Instead it operates on
IObservable<IObservable<T>>– a stream of observables – and it flattens the source toIObservable<T>by continually switching to the latest observable produced (and ignoring any values from the previous observables). It only completes when the outer observable completes and not the inner ones.This is exactly what you want – if a new value change occurs then ignore any previous results and only return the latest one.
Here’s how to do it.
First I removed the yucky event handling code into a couple of observables.
Your code seems to be a bit muddled. I assume that the value of the
DecimalUpDowncontrols are inputs to the server function that returns the result. So here are the functions that will call the server.Obviously you put in your actual server code calls in these two functions.
Now it is almost trivial to wire up the final observables and subscriptions.
The
DistinctUntilChangedmakes sure we only make the call if the values actually changed.Then it’s easy to call the two server functions, perform the
Switchand get back only the latest result which is then just assigned to the property.You may need to pop in a scheduler here or there and an
ObserveOnto get the subscription over to the UI thread, but otherwise this solution should work nicely.