Download source: RxTests.zip

No, this is not an attempt to get spidered by putting every hot technology in the title!  It’s actually self-induced attempt to give myself a brain hemorrhage by stuffing as many new to me technologies in one sample application as possible and trying to building it out …

In this corner, you have me!  Your humble author.  I will be learning Reactive Extensions (Rx) and digging deeper into the Model-View-ViewModel (MVVM) pattern.

And, in the other corner, you have a Windows Phone 7 sample application that uses Rx to retrieve data from an OData web service and display it into a data bound list all using the MVVM pattern and ready to support tombstoning using ViewModel pairing and Anchoring patterns.  (I would have used DynamicObject, but I don’t think it’s supported on the Phone at the moment.)

Here are some resources I used …

Windows Phone 7 Tools (RTM)
Reactive Extensions (Rx)
Introducing the Reactive Framework (in 5 parts!)
Introduction to MVVM (video)
(not yet) 101 Rx Samples
OData Client Library for Windows Phone 7
Silverlight Rx DataClient within MVVM
Netflix OData catalog
WCF Data Services

You will need to install Reactive Extensions for Silverlight in order to get the IObservable interface.  I am using the Silverlight 4 version.  You will need to reference the OData Client Library (System.Data.Services.Client) as well.

And, here is a preview of what I built …

image

Looks pretty simple, right?  It’s like a Russian nesting doll, you have to unpack a lot of stuff to fully understand it.

Let’s start at the beginning, the Solution …

image

(If you don’t have the Solution Navigator from the Productivity Power Tools, you should definitely install it!)

I started with a blank Windows Phone 7 project template and added Model and ViewModel folders.  I created the DataClient, MainViewModel and TitleViewModel classes.  To create the netflix class, I used the WCF Data Service Client Utility tool (usually in C:\Windows\Microsoft.NET\Framework\v4.0.30319\) as follows from a Visual Studio command line:

DataSvcutil.exe /uri:http://odata.netflix.com/Catalog/ /DataServiceCollection /Version:2.0 /out:c:\temp\netflix.cs

image 

This creates a set of classes on top of the Netflix OData service.  In my case, I copied the file from my c:\temp directory and moved it to my solution directory under Model and used “Add Existing Item…” from the IDE.

That’s the setup.  Let’s start from the top with App.xaml.cs.  I added a static field and property as follows …

private static MainViewModel _mainModel = null;

public static MainViewModel ViewModel
{
get
{
if (_mainModel == null)
{
_mainModel = new MainViewModel();
}

return _mainModel;
}
}

I basically took that from the Windows Phone Databound Application template.  The purpose is to have a ViewModel hanging off the App (i.e. available to any page in the application) to which other View Models can be Anchored.  This will setup the ability to simply persist the DataContext of any page during tombstoning.  You will see a small example of that with my TitleViewModel, but basically that is Sir Not Appearing in this Blog Post. 

image

(Much more on that pattern here.) Let’s move to the MainPage.xaml and MainPage.xaml.cs.  I just use some basic formatting I stole from ScottGu for the list …

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="MainListBox" ItemsSource="{Binding Titles, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<Image Source="{Binding ImageUrl}" Height="73" Width="73" VerticalAlignment="Top"
Margin="0,10,8,0"/>
<StackPanel Width="370">
<TextBlock Text="{Binding ShortName}" Foreground="#FFC8AB14" FontSize="28" />
<TextBlock Text="{Binding ShortSynopsis}" TextWrapping="Wrap" FontSize="24" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>

The important part here is the Data Binding (more on Data Binding than you ever wanted to know).  Basically, this says that the object that I will use to populate the list will have a public property called Titles.  The binding will be OneWay.  That means that we will populate the list only.  The user will not be changing the data. I will bind that object in the code by setting the DataContext of the page to that object.  That is done in the MainPage.xaml.cs file …

public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();

DataContext = App.ViewModel;
}
}

You will note that I have used the static property on the App class that we created above.  So, during the construction of the MainPage, the constructor for the MainViewModel will be called (that is the type of the ViewModel property on App).  Let’s take a look at MainViewModel.cs …

#region Default Using Statements
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
#endregion

using NetflixModel;
using RxTests.Model;

using System.Linq;
using System.Diagnostics;
using System.Data.Services.Client;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using Microsoft.Phone.Reactive;


namespace RxTests.ViewModel
{
public class MainViewModel
{
private ObservableCollection<TitleViewModel> _titles = null;
public IEnumerable<TitleViewModel> Titles
{
get { return _titles;}
}

public MainViewModel()
{
_titles = new ObservableCollection<TitleViewModel>();

_titles.CollectionChanged += (s, e) => { Debug.Assert(false); };

var client = DataClient.GetTitles(new Uri("http://odata.netflix.com/Catalog/"));

client.Subscribe( p =>
{
if (p.EventArgs.Error == null)
{
foreach (var t in
(DataServiceCollection<Title>)p.Sender)
{
_titles.Add( new TitleViewModel(t) );
}

Debug.Assert(_titles.Count == 5);
}
else
{
Debug.Assert(false);
}
});
}
}
}


I have an ObservableCollection of TitleViewModels.  An ObservableCollection is kinda like a piñata -- it shoots out stuff when you hit it.  In actuality, it will raise a CollectionChanged event when it is modified.  If you want to see that in action in the debugger, you can hook that event as follows …

_titles.CollectionChanged += (s, e) => { Debug.Assert(false); };

This will break into the debugger each time the Collection is modified.  I expose the Collection publically as an IEnumerable of TitleViewModels.  This makes sure that nobody mucks with the collection from outside my ViewModel (and thus nobody from the outside will raise the CollectionChanged event) in accordance with the OneWay nature of the data binding.

I then use a static member on the DataClient class (more on the DataClient class below) to get my list of Titles.  The GetTitles method calls for an argument of the Uri of the OData source, so I pass it in.  I get a reference back to an IObservable.  An IObservable has one method, Subscribe.  And this is where we get to the heart of our Rx learning and here it is …

An Observer Subscribes to an Observable to receive push-based notifications.

That’s like Rx in 12 words or less.  It’s like the analog of an IEnumerable.  I pull things from an IEnumerable, things get pushed from an IObservable.

Let’s look at the DataClient for more on that …

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using NetflixModel;
using System.Linq;
using RxTests.ViewModel;
using Microsoft.Phone.Reactive;
using System.Data.Services.Client;

namespace RxTests.Model
{
public static class DataClient
{
public static IObservable<IEvent<LoadCompletedEventArgs>> GetTitles(Uri uri)
{
var context = new NetflixCatalog(uri);
var titles = new DataServiceCollection<Title>();

// This is the guy I am goig to get push notifications from
var observable = Observable.FromEvent<LoadCompletedEventArgs>(h => titles.LoadCompleted += h,
h => titles.LoadCompleted -= h);

// This constructs the expression tree for the query that I want to make against the data
var query =
(from t in context.Titles
where t.Rating == "PG" && t.AverageRating > 4.0
select t).Take( 5 );

// Load my DataServiceCollection
titles.LoadAsync( query );

// Return my push notification source
return observable;
}
}
}


Don’t panic!  This is where we really need to wrap our minds around a concept or two.

First, we new up a NetflixCatalog.  That’s got all our WCF Service functionality in it (basically we are wrapping all the REST calls from our client to the OData server).

Then, we create a DataServiceCollection of Titles.  A DataServiceCollection is a fancy ObservableCollection that is used by a WCF Data Service to return data to a caller.  Titles is a class that is generated by the tool we used and lives in netflix.cs.  If you want to see the shape of an OData service, you can use a browser like IE8 or a tool like LINQPad

image

Then we get tricky …

// This is the guy I am going to get push notifications from
var observable = Observable.FromEvent<LoadCompletedEventArgs>
(h => titles.LoadCompleted += h,
h => titles.LoadCompleted -= h);

We create out Observable from an Event.  Basically, we say “Hey, I want to get notifications from a guy who raises an event that will have an object of type LoadCompletedEventArgs connected to it."  Wait!  I know such a guy!  It’s DataServiceCollection.LoadAsync!  That raises a LoadCompleted event that looks like this …

public event EventHandler<LoadCompletedEventArgs> LoadCompleted

LoadCompleted is an event that carries a LoadCompletedEventArgs object as its payload.  Cool!  So we create an observable on our DataServiceCollection that we called “titles” based on the results of the LoadCompleted event. At this point, however, nothing happens.  Someone has to “Subscribe” (express interest) for anything to happen.

Next, we use LINQ to build an expression tree (technically, an IQueryable of Titles) that represents the query we want to execute against our OData source …

// This constructs the expression tree for the query that I want to make against the data
var query =
(from t in context.Titles
where t.Rating == "PG" && t.AverageRating > 4.0
select t).Take( 5 );

Basically, I say give me the Titles that are rated “PG” and have an average rating of greater than 4.0 – I will take the first 5 of those (not necessarily the top 5 … I just say Take 5).  I pass that query to our LoadAsync method and return the guy we want to observe.

Now, let’s finish our look at MainViewModel.  When we left our hero, he had just asked the DataClient for something that will give push notifications (an IObservable) based on an event that has a LoadCompletedEventArgs attached.  DataClient happily made one and returned it.  Now MainViewModel tells the observable that he is interested in what is happening and what to do when something happens.

client.Subscribe( p =>
{
if (p.EventArgs.Error == null)
{
foreach (var t in
(DataServiceCollection<Title>)p.Sender)
{
_titles.Add( new TitleViewModel(t) );
}

Debug.Assert(_titles.Count == 5);
}
else
{
Debug.Assert(false);
}
});

The overload of Subscribe we are calling takes an Action<T> delegate or in this case Action<IEvent<LoadCompletedEventArgs>>.  An IEvent is actually from Microsoft.Phone.Reactive and is pretty light, your typical Sender/EventArgs combo.

How do we know what to do?  Well, remember that an Action<T> delegate eats Ts and returns nothing.  So, we need to pass a method that will take a T (or in this case an IEvent<LoadCompletedEventArgs>) and do something with it.  We need to populate our ObservableCollection of TitleViewModels so that’s what we do.

Once we populate that ObservableCollection, the data binding kicks in and the ListBox in our UI is populated!

For completeness, my TitleVewModel is a very thin wrapper on just the data that we need from our data level object (Title) – basically, an Adapter with TitleViewModel as the target and Title as the adaptee .

public class TitleViewModel
{
Title _impl = null;

public TitleViewModel( Title t )
{
_impl = t;
}

public string ShortName
{
get { return _impl.ShortName; }
}

public string ShortSynopsis
{
get { return _impl.ShortSynopsis; }
}

public string ImageUrl
{
get { return _impl.BoxArt.SmallUrl; }
}
}

And there you have it!  This is a very good general template for a Windows Phone 7 data driven application that maintains a very responsive UI and goes and gets data out of a backend service.