WPF problem with subscription
-
Wednesday, January 23, 2013 2:57 PM
Could someone explain this to me. I have a simple button in a wpf form that when first pressed creates a Subject observable, subscribes to a observable collection that is bound to a listbox, then falls through and does an OnNext. The next button press, just calls OnNext. The first OnNext is lost but the remaing are displayed. Why (PS I would never program this way, but it came from another problem)
private Subject<DateTime> subject; private readonly ObservableCollection<string> theCollection = new ObservableCollection<string>(); private void SimpleStartButton(object sender, RoutedEventArgs e) { if (this.subject == null) { this.subject = new Subject<DateTime>(); this.subject.SubscribeOn(Scheduler.Default) .ObserveOnDispatcher() .Subscribe(c => this.theCollection.Add(c.ToString())); } this.subject.OnNext(DateTime.Now); }My thinking is that the subscription doesn't happen until the Dispatcher thread is run, but it is only a guess.
Is there anyway around this problem
Thanks
-jam
All Replies
-
Wednesday, January 23, 2013 3:06 PM
I also discovered that if I put a Thread.Sleep(10) after the subscribe, it works. If I put a Thread.Sleep(1), it does not. So I am now thinking that its the startup of the SubscribeOn that is causing the problem. But this is a very serious bug since I don't know how to wait before the first OnNext
-jam
-
Friday, January 25, 2013 3:06 AM
Hi Jam,
> So I am now thinking that its the startup of the SubscribeOn that is causing the problem.
Correct. SubscribeOn is introducing concurrency because you're passing in Scheduler.Default. Thus, you've introduced a race condition. Putting the UI thread to sleep allows the subscription to complete before the first OnNext is called. How long you have to sleep depends upon many factors controlled by Windows.
First of all, you should probably never use Scheduler.Default. Instead, choose a concrete scheduler that meets your particular requirements.
Secondly, SubscribeOn is rarely needed. In your particular example, it's not needed at all. It serves no purpose except to introduce unnecessary concurrency. It's unnecessary because in this example you want to subscribe synchronously, not asynchronously. Note that SubscribeOn in this example has no effect on notifications; i.e., notifications are still going to be pushed out of the subject on the thread in which OnNext is called.
Since you're calling OnNext on the UI thread, ObserveOnDispatcher isn't required either.
Remove SubscribeOn from your query and it will behave as you had expected.
Also remove ObserveOnDispatcher from your query and it will still behave as expected.
- Dave
- Proposed As Answer by LeeCampbell Friday, January 25, 2013 1:24 PM
-
Friday, January 25, 2013 1:28 PM
Sorry I just had a quick giggle to myself as I restructured the code in my head as per Dave's (very valid) suggestions.
private Subject<DateTime> subject; private readonly ObservableCollection<string> theCollection = new ObservableCollection<string>(); private void SimpleStartButton(object sender, RoutedEventArgs e) { if (this.subject == null) { this.subject = new Subject<DateTime>(); this.subject//.SubscribeOn(Scheduler.Default) //.ObserveOnDispatcher() .Subscribe(c => this.theCollection.Add(c.ToString())); } this.subject.OnNext(DateTime.Now); }So first remove the unnessecary Subscribe on and ObserveOn
private Subject<DateTime> subject; private readonly ObservableCollection<string> theCollection = new ObservableCollection<string>(); private void SimpleStartButton(object sender, RoutedEventArgs e) { if (this.subject == null) { this.subject = new Subject<DateTime>(); this.subject.Subscribe(c => this.theCollection.Add(c.ToString())); } this.subject.OnNext(DateTime.Now); }But now look at the code, why would it not just be
private readonly ObservableCollection<string> theCollection = new ObservableCollection<string>(); private void SimpleStartButton(object sender, RoutedEventArgs e) { this.theCollection.Add(DateTime.Now.ToString()) }
This is semantically the same. Anyway, it made me giggle, Rx+WPF makes people write the strangest code.
Lee Campbell http://LeeCampbell.blogspot.com
- Marked As Answer by ophioscorodon Friday, January 25, 2013 2:14 PM
-
Friday, January 25, 2013 2:20 PM
Thanks Lee
After thinking about my original problem for a while (As I said, this was not my original problem but had to do with a INotify property that was made into an observable and then set from a non-gui thread) I realized you were correct about subscribeOn, I don't need it whatsoever. However I do need the ObserveOn because the threads setting the INotify property are not GUI)
Actually its more complicated than this, the INotify properties go into a LINQ statement that pop out a value that enables/disables a button in the GUI.
Thanks for pointing out my misunderstanding of SubscribeOn!
-jam

