Reputation: 22406
I'm trying to follow along with Jonathan Worthington's airport announcement example in An Event-driven and Reactive Future
It compiles.
The problem: SayGateChange
is never called. I'm new to Rx. I must be leaving something out. What I have here is his code as exactly as I could transcribe it. Sadly, there is no source available online.
AddGateChange
is supposed to push a new item onto EventStreams.GateChanged
, which in turn is supposed to be watched by Announcer.Announcements
, which is supposed to be watched by SayGateChange
.
I'm in Windows forms, not WPF, if that makes a difference.
I will gladly put it into a console app or LinqPad if that will make it work.
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
public class frmAnnouncements
{
Announcer _Announcer = new Announcer();
ObservableCollection<string> Announcements = new ObservableCollection<string>();
private void frmRx_Load(System.Object sender, System.EventArgs e)
{
PopulateAnnouncements();
AddGateChange();
}
private void AddGateChange()
{
EventStreams.GateChanged.OnNext(new GateChanged {
Destination = "DAL",
FlightCode = 1503
});
}
private void PopulateAnnouncements()
{
_Announcer.Announcements.ObserveOnDispatcher().Subscribe(SayGateChange);
}
private void SayGateChange(string Message)
{
Interaction.MsgBox(Message);
}
public class GateChanged
{
public string FlightCode;
public string Destination;
}
public class EventStreams
{
public static Subject<GateChanged> GateChanged = new Subject<GateChanged>();
}
public class Announcer
{
public Announcer()
{
this.Announcements = EventStreams.GateChanged.Select(e => string.Format("gate change {0} to {1} ", e.FlightCode, e.Destination));
}
public IObservable<string> Announcements;
}
public frmAnnouncements()
{
Load += frmRx_Load;
}
}
Upvotes: 2
Views: 834
Reputation: 29776
As @Enigmativity stated, using ObserveOnDispatcher()
is a problem - although without looking at Interaction.MsgBox
its hard to be 100% certain it's the whole story - I guess it may be in the video, but it's rather long and I didn't watch it all.
The use of ObservableOnDispatcher()
suggests you have pulled in the wrong nuget package for Rx:
rx-xaml
(deprecated synonym rx-wpf
), which provides the extension method ObserveOnDispatcher()
rx-winforms
, which provides the extension method overload ObserveOn(Control)
Both Winforms and WPF have a similar design where the user interface runs on a dedicated thread. In Winforms this is known as the "UI Thread" and in WPF as the "Dispatcher". Although the concept is very similar, the implementation is quite different.
ObserveOnDispatcher
in WPF will cause the observer notifications OnXXX
to be invoked on the dispatcher thread.
In WinForms, where you use ObserveOn(this)
, the this
will generally be the form itself. For any WinForms control, this will locate the control's SynchronizationContext
and Post OnXXX
notifications to that.
Both overloads are smart in that invocations are direct if you happen to be on the correct Dispatcher thread or UI thread already.
I do seem to remember that WinForms is a lot more tolerant of updating UI off the UI thread - although this problem occurs in WPF too. This isn't a good thing, since it can lead to unpredictable results that are hard to debug. I note that the WinForms MessageBox.Show
method, for example, doesn't care which thread it is invoked on since it creates it's own window. In general, use of some form of ObserveOn
/ObserveOnDispatcher
is always recommended in UI scenarios.
For this reason, it's a good idea to understand how these work in detail. For this, and to learn about the related SubscribeOn
, have a look at this question.
I am surprised that you didn't get an informative InvalidOperationException
stating that "The current thread has no Dispatcher associated with it." I can only think some other part of your code is swallowing exceptions, or you are using WPF code in your app as well and a Dispatcher
had been created associated with the Winforms UI thread. That code behind Interaction.MsgBox
is probably to blame for swallowing an error. Either way, I suggest removing rx-xaml
to avoid confusion.
Upvotes: 1