I’ve gotten rather fond of using the Reactive Extensions for populating my data from the web in my Windows Phone applications. They provide a lot of flexibility and the code stays in a readable form.
To demonstrate in a traditional MVVM model, let’s drop some movie information from Netflix onto our Windows Phone screen. First we need a structure to hold that movie data.
We also need a view model for the view to bind to. The view model will expose two interesting properties for the the UI use. The NetworkBusy property can be bound to a progress bar to automatically show/hide the bar while the underlying network service is busy. The Movies property can be bound to a listbox so that the user can scroll through the list.
If our service class provides the view model with an IObservable<MovieData> when we ask for all the movies, we can just subscribe to the stream of MovieData objects it provides and add them to our Movies collection. Furthermore, since an Observable sequence provides error and completion notifications, we will know exactly when to disable the progress bar the UI uses by setting NetworkBusy to false. We can also take action on errors as needed. So we’d like it to be as simple as this.
Also, note that the only thing we need to do to avoid accessing the UI on the wrong thread is to add ObserveOnDispatcher() to the mix.
Next we need to build the method on the MovieService class that will generate the Observable stream of MovieData objects for the view model. In this case we will return all the films that have a rating greater than 4. Here is the code for the method. I’ll break it down after the snippet.
Lines 4-5: create the WebClient and subscribe to the event in a manner that provides us an Observable. Because this wraps the even, ‘o’ is of type IObservable<DownloadStringCompletedEventArgs>
Once the download is initiated we provide some filtering and projection to the current sequence to transform it to an IObservable<MovieData> and return that to the user. The consumer doesn’t want to deal with EventArgs, he wants MovieData!
Line 11: Take(1) - Normally observables wrapped around events never complete. Adding Take(1) provides us the completion event because we know the event will only be raised once. 1 web request = 1 web response.
Line 11: SelectMany(…) – The single response of the web request is a string that contains multiple movies. If we just did Select() and returned a collection of MovieData objects, the consumer would have to loop over collection. If we use SelectMany(), the collection gets flattened out and we will return just the MovieData objects in the sequence.
Lines 15-21: take the resulting downloaded string and transform it with LINQ to XML into a collection of MovieData objects
Additional Fun: This on its face is a nice enough pattern, but there are a couple of others patterns which can emerge nicely on top of this one.