This article is compatible with the latest version of Silverlight.
This is part 1 of the series “Reactive Extensions in Silverlight”.
1. Introduction
One of the coolest features which is part of .NET Framework and also available for Silverlight applications is the RX Framework. The arising interest around the RX Framework made me roll up my sleeves and start playing around this.
So the first step was to answer several important questions such as: “what is Linq to Events?”, “what is RX Framework?”, and “what is Reactive Programming?” And generally what lies behind these sound terms? Linq to events or RX Framework (also known as Reactive Extensions for .NET Framework) is one and the same designation for a library for composing asynchronous and event-based programs using observable collections. Ok, this is the official description, but it sounds pretty academic and initially it didn’t give me any understanding about the RX Framework. The next step was to watch several introduction videos on Channel 9; they helped me to understand better the meaning (the idea) of the framework.
Take a look at the next code snippet:
How many times have you done this in your application? Consider for a moment the collections in .NET Framework; they are all unified by IEnumerable. Arrays, lists, they all implement the IEnumerable interface. If you take a look at these collections, they are pulling collection. If you are using a loop, you receive the data from a collection over a period of time, or in other words, you are pulling the data, saying “move next, move next”, pulling the elements out.
What we have here is an implementation of the Iterator pattern (IEnumerable\IEnumerator).
Now, if we take a look at the RX style collection, they are push collections. They push values to you. And here is another key moment to understand. What does “push values to you” mean? Take a look again at the next code snippet. It represents another very common situation in the programming, namely, the usage of events.
We are handling the MouseMove event. This is a pretty standard task. The mouse cursor’s location value is changing over a period of time, and an event notifies you about that - or in other words, the source is pushing values to you. If we again refer to the design patterns, we will see that we have an implementation of the Observer pattern. Yes, the centerpiece of the RX Framework is the Observer pattern. All RX style collections are unified by the IObservable interface. You can then subscribe to this observable using an instance of an object implementing the IObserver interface.
On one side – we have the Iterator pattern ( IEnumerable<T>\IEnumerator<T>) and on the other side we have the Observer pattern (IObservable<T>\IObserver<T>). The interesting point here is that the RX developers consider the IObservable<T> as a mathematical dual to the IEnumerable<T> interface. Normally, any Enumerable collection can be turned into Observable collection and vice versa. Basically the RX Framework consists of two interfaces - IObservable<T> and IObserver<T>. Just like the Enumerable collections where you can perform various LINQ operations, the same are implemented for the Observable collections.
So if you have to remember something, it's that the Enumerable collections are pulling data from the source and the Observable collections are pushing data from the source. And any Enumerable collection can be turned into an IObservable collection and vice versa.
Ok, But what can be done with the RX Framework?
Probably, each time I am playing with a new technology, the part that is most difficult for me is to put the technology into practice. What kind of problems can be solved with the RX Framework? In fact the RX Framework doesn’t solve problems that were unsolvable in the past, but it offers new, different and sometimes easier ways to approach existing problems:
- Asynchronous programming. RX provides you with a built-in threading support and lets you observe collections in background threads.
- Composing custom events. This is probably the most exciting feature of the RX Framework (at least for me). Imagine a drawing application. The most common tasks in such sort of applications are to select, draw and modify objects. The core part of these behaviors is the mouse and keyboard interaction, combining mouse buttons, implementing dragging operations, etc. The RX Framework addresses these issues.
- RX offers two new interfaces: IObservable<T> and IObserver<T>. You could create observable from existing event and enumerable, use it as a regular CLR class.
Enough words, let’s take a look at some demos. Before proceeding you could download the source code for the demos from here.
Download source code
The RX Framework can be downloaded from here.
2. From IEnumerable<T> to IObservable<T>
We can start with something simple. As I said in the introduction, any “enumerable collection can be turned into observable collection”. Let’s see what this means. For the first demo I am using two simple ListBoxes, which are populated in the code-behind. You could check the source code for this example in the Demo1 folder of the Demo Solution.
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox x:Name="lbEnumerable"/>
<ListBox x:Name="lbObservable"
Margin="10"/>
</Grid>
The first ListBox is populated through a foreach loop.
List<string> products = new List<string>
{
"Chai", "Chang", "Aniseed Syrup", "Konbu", "Tofu", "Geitost"
};
foreach ( var item in products )
this.lbEnumerable.Items.Add( item );
And now the interesting part – the RX Framework exposes a ToObservable() extension method, which turns an IEnumerable collection into an IObservable collection.
IObservable<string> observable = products.ToObservable();
Once you have an Observable you can subscribe in the following fashion:
observable.Subscribe<string>(
p =>
{
this.lbObservable.Items.Add( p );
} );
The result from the previous code-snippet is that the lbObservable ListBox is populated with items thanks to the reactive framework, which pushes values to you (to the ListBox).
Another interesting extension method is Repeat(), which repeats the observable sequence “n” times. Or even more, you can repeat sequences indefinitely.
IObservable<string> observable = products.ToObservable().Repeat(10);
The result is that all products will be pushed to the ListBox ten times.
There are several overloads of the Subscribe method. One of them accepts a delegate which is executed when the observable sequence ends.
observable.Subscribe<string>(
p =>
{
this.lbObservable.Items.Add( p );
}, () =>
{
this.lbObservable.Items.Add( "-----Completed----" );
} );
And the result is:
3. Unsubscribing from Observables
An important issue is to be able to unsubscribe from the observer. Take a look at the Repeat() example again. And for some reason you would like to create an indefinite sequence.
IObservable<string> observable = products.ToObservable().Repeat();
The result from the above example is that the observable collection will push elements to the observer infinitely. Each time the Subscribe() method is called, it returns an IDisposable objects that can be used to unsubscribe and to stop pushing elements to the observer, like this:
IDisposable disposable = observable.Subscribe<string>(
p =>
{
this.lbObservable.Items.Add( p );
}, () =>
{
this.lbObservable.Items.Add( "-----Completed----" );
} );
//....
disposable.Dispose();
4. Turning Events into Observables
This is one of the coolest features of the RX Framework. The Observable object exposes a FromEvent () static method, which can be used to turn an event into an observable. After that, the event can be used as any other regular object in your application.You could check the source code for this example in the Demo2 folder of the Demo Solution.
var mouseMoveEvent = Observable.FromEvent<MouseEventArgs>( this, "MouseMove" );
And of course you could subscribe to the observable of the mouse move event in the familiar fashion.
mouseMoveEvent.Subscribe(
p =>
{
// Do something when the mouse
// moves around the screen
} );
This is pretty simple and couldn’t illustrate the power of the RX Framework. You could attach to the mouse move event in the standard and old school manner.
But let’s complicate the situation a little bit. Imagine that you want to update the text box only when you are moving the mouse around the screen and in the same time the left mouse button is down. Which means that if you use the standard approach for attaching to events, you should have an additional Boolean flag which is raised each time the left mouse button is pressed and to reset it when the left mouse button is released. Take a look at how this can be handled using reactive extensions. The only thing you should do is to create observables from the MouseMove and MouseLeftButtonDown events, and to combine them using the SkipUntil() extension method.
var mouseMoveEvent = Observable.FromEvent<MouseEventArgs>( this, "MouseMove" );
var mouseLeftButtonDown = Observable.FromEvent<MouseButtonEventArgs>( this, "MouseLeftButtonDown" );
var events = mouseMoveEvent.SkipUntil( mouseLeftButtonDown );
// Subscribe to the events
The final step is to stop observing, when the mouse left button is released. Again this can be done in the same manner – we are observing the MouseLeftButtonUp event. However, this time the TakeUntil() extension method will be used.
var mouseMoveEvent = Observable.FromEvent<MouseEventArgs>( this, "MouseMove" );
var mouseLeftButtonDown = Observable.FromEvent<MouseButtonEventArgs>( this, "MouseLeftButtonDown" );
var mouseLeftButtonUp = Observable.FromEvent<MouseButtonEventArgs>( this, "MouseLeftButtonUp" );
var events = mouseMoveEvent.SkipUntil( mouseLeftButtonDown ).TakeUntil( mouseLeftButtonUp ).Repeat();
events.Subscribe(
p =>
{
Point mousePosition = p.EventArgs.GetPosition( this );
tbMousePosition.Text = String.Format( "Mouse position {0}, {1}",
mousePosition.X, mousePosition.Y );
} );
What we have here is that the text block is updated only when the mouse is moving and the left button is pressed. We will stop updating the text box when the mouse left button is released.
5. Drag and Drop
Now, as we have some basic knowledge about the reactive extensions, lets create the equivalent of the “Hello World” for RX, namely, this is a drag and drop application. At the first moment I said: “wow drag and drop, this is not simple”. But you’ll see how easy it could be with RX.
To begin, I’ll add a TextBox and Image inside a Canvas. You could check the source code for this example in the Demo3 folder of the Demo Solution.
<Canvas>
<TextBlock x:Name="textBlock"
Text="Drag Me!!!"
FontSize="20"
Canvas.Top="10"
Canvas.Left="10"/>
<Image Source="/RXDemos;component/SilverlightShow.png"
Canvas.Top="50"
Canvas.Left="10"
x:Name="image"/>
</Canvas>
Next, turn the MouseLeftButtonDown, MouseMove and MouseLeftButtonUp events into observables.
var mouseMoveEventImage = Observable.FromEvent<MouseEventArgs>( image, "MouseMove" );
var mouseLeftButtonDownImage = Observable.FromEvent<MouseButtonEventArgs>( image, "MouseLeftButtonDown" );
var mouseLeftButtonUpImage = Observable.FromEvent<MouseButtonEventArgs>( image, "MouseLeftButtonUp" );
You can create a new observable to listen for a sequence of mouse positions – starting when the mouse left button goes down, and then listening to all positions in mouse move, until the mouse left button goes up.
var draggingEventsImage = mouseMoveEvent.SkipUntil( mouseLeftButtonDown ).
TakeUntil( mouseLeftButtonUp ).Repeat();
As I mentioned in the introduction, you could turn an event into observable and treat it as any regular CLR object. Which means that you could write LINQ queries against it. Now check this out.
var draggingEventsImage = from pos in mouseMoveEventImage.SkipUntil( mouseLeftButtonDownImage ).
TakeUntil( mouseLeftButtonUpImage )
.Let( mm => mm.Zip( mm.Skip( 1 ), ( prev, cur ) =>
new
{
X = cur.EventArgs.GetPosition( this ).X -
prev.EventArgs.GetPosition( this ).X,
Y = cur.EventArgs.GetPosition( this ).Y -
prev.EventArgs.GetPosition( this ).Y
} ) ).Repeat()
select pos;
What I am doing here is simply taking the delta between the previous position and the current position, while the mouse is being dragged. The Zip functions merge two observables sequences into one observable sequences by using a selector function.
Finally, subscribe to the dragging events and set the new position of the image.
draggingEventsImage.Subscribe(
p =>
{
Canvas.SetLeft( image, Canvas.GetLeft( image ) + p.X );
Canvas.SetTop( image, Canvas.GetTop( image ) + p.Y );
} );
You could do exactly the same for the TextBlock or any other control inside the canvas.
6. Drawing with RX
Considering the previous drag and drop demo, now we can examine another common issue that is addressed by the RX Framework. You could create drawing application in the same manner, as in the drag and drop demo. You could check the source code for this example in the Demo4 folder of the Demo Solution.
var mouseMoveEvent = Observable.FromEvent<MouseEventArgs>( this, "MouseMove" );
var mouseLeftButtonDown = Observable.FromEvent<MouseButtonEventArgs>( this, "MouseLeftButtonDown" );
var mouseLeftButtonUp = Observable.FromEvent<MouseButtonEventArgs>( this, "MouseLeftButtonUp" );
var draggingEvents = from pos in mouseMoveEvent.SkipUntil( mouseLeftButtonDown ).
TakeUntil( mouseLeftButtonUp )
.Let( mm => mm.Zip( mm.Skip( 1 ), ( prev, cur ) =>
new
{
X2 = cur.EventArgs.GetPosition( this ).X,
X1 = prev.EventArgs.GetPosition( this ).X,
Y2 = cur.EventArgs.GetPosition( this ).Y,
Y1 = prev.EventArgs.GetPosition( this ).Y
} ) ).Repeat()
select pos;
draggingEvents.Subscribe(
p =>
{
Line line = new Line();
line.StrokeThickness = 2;
line.Stroke = new SolidColorBrush( Colors.Black );
line.X1 = p.X1;
line.Y1 = p.Y1;
line.X2 = p.X2;
line.Y2 = p.Y2;
this.LayoutRoot.Children.Add( line );
});
7. Asynchronous Programming with RX – the PI Calculator
Asynchronous programming is by no means restricted to Web scenarios. But not anymore, RX is ideal for composing async applications.
Let’s say that the value of PI in System.Math.PI, at only 20 digits, isn’t precise enough for you. In that case, you may find yourself writing an application that calculates pi to an arbitrary number of digits. Although this may not be a real world example, it will illustrate the idea of handling asynchronous programming with RX. Probably many of your applications need to perform long-running operations (e.g.: printing, web service call, or calculation).
You could check the source code for this example in the Demo5 folder of the Demo Solution.
Check out the method, which is responsible for the pi calculation.
static IEnumerable<CalculationData> Calculate( int numberOfDigits )
{
for ( int i = 0; i < numberOfDigits; i += 9 )
{
int nineDigits = NineDigitsOfPi.StartingAt( i + 1 );
int digitCount = Math.Min( numberOfDigits - i, 9 );
string ds = string.Format( "{0:D9}", nineDigits );
yield return new CalculationData( ds.Substring( 0, digitCount ),
i + digitCount );
}
}
As you know, any IEnumerable collection could be turned into IObservable collection, and could be observed in a background thread.
IObservable<CalculationData> digits = Observable.ToObservable<CalculationData>(
Calculate( this.InputDigitsCount ) );
digits.Subscribe<CalculationData>(
item =>
{
if ( String.IsNullOrEmpty( this.Output ) )
this.Output = "3.";
this.Output = String.Concat( this.Output, item.NextDigits );
}, () =>
{
this.IsCalculationInProgress = false;
} );
8. Final Words
The RX Framework doesn’t solve any unsolvable problems, but it could be really useful. It offers you a different approach for any existing problems. I hope that this article has offered you a good overview of the RX Framework, although any of the fundamentals are not covered.
9. References
- http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx
- http://channel9.msdn.com/tags/Rx/