Feeds:
Posts
Comments

Posts Tagged ‘MVVM’

In my last post I demonstrated how to use reactive extensions in Silverlight to easily build responsive UIs. In this post I am going to expand on that and demonstrate how to use reactive extensions (Rx) from within a view model by asynchronously loading an RSS feed into your model and populating the UI while the bytes are still coming in. This is about as fast and responsive as it gets and as you will see, by using Rx the code is concise and relatively easy on the eyes.

Note: All of this can be done without reactive extensions. Nothing here is new from the end users point of view. But as programmers, we should always be looking for ways to create more readable code in fewer lines to accomplish tasks even if we already know how to do them in other ways. Rx has the advantage of enabling you to easily write multi-threaded code without creating Thread objects and avoid using local variables or constructing user state objects to maintain state between events. Enough talk, time to make my case with code.

Note: If you want to get directly to the Rx stuff, skip down to the DataClient section.

First, let’s set up the foundation of a demonstration app that reads an RSS feed and displays the results to the user. Regardless of whether you’re going to use Rx or not, you’ll need to create the following:

1. The model. We clearly need a model to hold each RSS item. I’m going to use XmlSerializer to deserialize the objects from Xml, so I’ll add a bunch of XmlSerializer attributes to my class to help map the RSS Xml.

[XmlRoot(“item”)]

public class RssItem

{

    [XmlElement(“title”)]

    public string Title { get; set; }

    [XmlElement(“link”)]

    public string Link { get; set; }

    [XmlElement(“pubDate”)]

    public string PublishDate { get; set; }

    [XmlElement(“description”)]

    public string Description { get; set; }

 

    public string PlainTextDescription

    {

        get { return StripHtml(Description); }

    }

}

Note: Because RSS item descriptions often contain HTML, I created a read-only property that strips out the html tags from the description. Thanks to John Papa for providing the StripHtml algorithm in his book.

2. A routine to turn a stream into RSS item objects. There are different ways to accomplish this (such as using LINQ to XML) but we need a method that allows us to read the bytes as they become available. Therefore, I’ll use a combination of XmlSerializer and XmlReader in order to support a stream based approach and avoid waiting for the entire Xml document to be delivered before we can start processing it.

public static IEnumerable<RssItem> GetRssItems(XmlReader reader)

{

    XmlSerializer rssItemSerializer = new XmlSerializer(typeof(RssItem));

    while (reader.GoToElement(“item”))

        yield return rssItemSerializer.Deserialize(reader) as RssItem;

}

Note: I’ve created an extension method (.GoToElement) for the XmlReader class in order to make using XmlReader easier. Download the source to see this method.

3. A routine to create and return a WebRequest object.

private static WebRequest GetWebRequest(Uri Uri) {

    var Result = (HttpWebRequest)WebRequest.Create(Uri);

    Result.AllowReadStreamBuffering = false;

    return Result;

}

Note: I also set AllowReadStreamBuffering to false in order to prevent the response from being buffered, thus allowing us to start using the data as it is being downloaded.

Also Note: I’m using a WebRequest object instead of a WebClient object for 3 reasons: 1) the response callback occurs on a background thread 2) Silverlight only allows us to process unbuffered data on a background thread 3) Rx has an extremely simple way to turn async patterned calls into observable objects (more on this below).

4. The view. We will create our view the exact same way regardless of whether we use Rx or not. This is important because it allows a designer to build the view without caring about how we go about getting our data. This is a typical and simple M-V-VM view that merely binds a property from the view model to a control on the view. The important thing to take away here is that using Rx in this example does not affect our view in any way, shape or form.

<UserControl.DataContext>
    <local:ViewModel />
<UserControl.DataContext>
<ScrollViewer VerticalScrollBarVisibility="Auto"> 
    <ItemsControl ItemsSource="{Binding RssItems}"> 
        <ItemsControl.ItemTemplate> 
            <DataTemplate> 
                <StackPanel> 
                    <HyperlinkButton Content="{Binding Title}" FontSize="16" NavigateUri="{Binding Link}" Margin="3" /> 
                    <StackPanel Orientation="Horizontal" Margin="3"> 
                        <TextBlock Text="Posted: " Foreground="Gray" /> 
                        <TextBlock Text="{Binding PublishDate}" Foreground="Gray" /> 
                    </StackPanel> 
                    <TextBlock Text="{Binding PlainTextDescription}" TextWrapping="Wrap" Margin="3" /> 
                </StackPanel> 
            </DataTemplate> 
        </ItemsControl.ItemTemplate> 
    </ItemsControl>
<ScrollViewer>

The view simply shows our RSS items in a templated ItemsControl and wraps the whole thing in a ScrollViewer.

5. The ViewModel’s public API. The ViewModel needs to provide RssItem objects, so all we need is a property that can be bound to the view:

private ObservableCollection<RssItem> rssItems;

private ObservableCollection<RssItem> rssItems;

public IEnumerable<RssItem> RssItems

{

    get { return rssItems; }

}

So far everything above is probably identical to how you would have built the app regardless of whether or not you were going to use Rx! This is all standard stuff but I wanted to list everything involved in order to stress just how much of your code and coding practices do NOT need to change in order to take advantage of Rx. Now we have most of the major pieces of our demonstration app all nice and isolated and ready to be used with or without Rx.

The only things left are the DataClient (the thing that actually goes and gets the RSS Xml and populates your model) and the code in the ViewModel to call that DataClient.

The DataClient: Rx Magic time!

public static IObservable<RssItem> GetRssItems(Uri Uri)

{

    return

        (from request in Observable.Return(GetWebRequest(Uri))

        from response in Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)()

        from item in Mapper.GetRssItems(XmlReader.Create(response.GetResponseStream())).ToObservable()

        select item).ObserveOnDispatcher();

}

That’s it! We just need one great big expression to return something that our ViewModel can use. No events, no lambda, no long routines.

The purpose of this function is super simple: to return an IObservable<RssItem> whose job is to “push” fully populated RSS item objects out to the consumer one at a time as soon as they are available. Later I’ll show you how to consume an IObservable object.

The basic mechanics of this function is to combine IObservable objects using a Reactive LINQ expression and return one super IObservable object that performs its duties on .Subscribe and pushes out results one at a time back to the consumer.

Let’s dissect:

First we have:

Observable.Return(GetWebRequest(Uri))

This calls GetWebRequest (described earlier) to get a standard WebRequest and puts it into an IObservable object that does one simple thing: it “pushs” that single WebRequest object to it’s consumer.

By using Reactive LINQ notation:

from request in Observable.Return(GetWebRequest(Uri))

we can chain together other operations that use that WebRequest to create their own IObservable objects.

This might be tough to get your head around but will become clearer as we keep going.

Next, we take that request and use it in our next expression:

Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)()

This feature of Rx is awesome! Here we call the FromAsyncPattern method and pass in references to 2 methods that already exist on the WebRequest object. FromAsyncPattern wraps this in an IObservable object that does a few things… It calls BeginGetResponse when someone subscribes to it, it handles the async callback, calls EndGetResponse, and finally returns the result via a “push” to the subscriber.

In a nutshell, FromAsyncPattern hides all the plumbing of using the async pattern and wraps it in a nice IObservable object. One thing to note: The call out to the web does NOT get initiated until someone subscribes to the resulting IObservable. This gives the consumer the ability to control the timing.

Quick recap on our giant expression: At this point, we now have  a new IObservable object that works like this…

When someone subscribes to the resulting IObservable, it goes and subscribes to the first IObservable in our Reactive LINQ statement, which immediately “pushes” notification out that tells us that we have a web request. That push notification is handled by the statement in the Reactive LINQ statement which uses that request object to initiate and handle the asynchronous call to the server. When it is done, it “pushes” the response on to the subscriber (the subscriber being the next thing in our big ‘ol Reactive LINQ chain). Which is…

GetRssItems(XmlReader.Create(response.GetResponseStream()))

The first thing you may notice is that there’s no Rx in this expression. All it is doing is creating an XmlReader from the response stream and passing it into the function that creates models from an XmlReader. At this point we just have an IEnumerable<RssItem> on which we call…

.ToObservable()

ToObservable is an extension method that extends IEnumerable and turns it into an IObservable. This allows a consumer to get notified when the next item in the collection is available instead of making us loop through the collection manually. We also need an IObservable because we’re chaining together IObservable expressions. You can’t just switch to IEnumerables in the middle of your LINQ expression. For example. A traditional LINQ expression with IEnumerable might look like:

from purchases in customers

from order in purchases

select order;

but you can’t start with an IObservable and switch to an IEnumerable or vice versa…

Finally, we want to select the item that ultimately will be pushed back to the consumer and wrap our IObservable in a new one that performs all “push” operations on the dispatcher. This is important because WebRequest returns all its data on a background thread and so without it, push notifications will occur on that same thread and throw an exception when we try to modify the UI.

select item).ObserveOnDispatcher();

Note: We could also have built the guts of this procedure using more traditional methods like handling events by creating our own class that implemented the IObservable interface. However, I think it’s better to re-use methods like FromAsyncPattern to do as much of the leg-work for us as possible.

Back to the ViewModel:

public ViewModel()

{

    rssItems = new ObservableCollection<RssItem>();

    IObservable<RssItem> client = DataClient.GetRssItems(new Uri(http://pheedo.msnbc.msn.com/id/3032091/device/rss/io&#8221;));

    client.Subscribe(item => rssItems.Add(item));

}

Done! All we had to do is get the IObservable object from the DataClient, subscribe to it and for each item returned, and add that item to the ObservableCollection bound to the view.

And remember, because this is running on a background thread and not buffering the response from the server, we can easily be spitting back model objects and showing them in the UI as more bytes are still being downloaded and processed.

More Rx magic!

OK, so we have an ultra responsive UI that shows data as soon as physically possible and we have a super slim DataClient that leverages Rx to avoid writing our own code to call and handle asynchronous webrequests. But what else can we do with Rx in the context of this app?

A: “a ton! “ but this post is already getting long so I’ll get you going with a couple quick examples and let your imagination take over.

Imagine you wanted to combine two different RSS feeds:

public ViewModel()

{

    rssItems = new ObservableCollection<RssItem>();

    var TopNewClient = DataClient.GetRssItems(new Uri(http://pheedo.msnbc.msn.com/id/3032091/device/rss/io&#8221;));

    var BusinessNewsClient = DataClient.GetRssItems(new Uri(http://pheedo.msnbc.msn.com/id/3032221/device/rss/&#8221;));

    var client = Observable.Merge(TopNewClient, BusinessNewsClient);

    client.Subscribe(item => rssItems.Add(item));

}

Or how about you wanted to expose an IsBusy state to the client:

< toolkit:BusyIndicator IsBusy=”{Binding IsBusy}” />

 public ViewModel()

{

    rssItems = new ObservableCollection<RssItem>();

    IObservable<RssItem> client = DataClient.GetRssItems(new Uri(http://pheedo.msnbc.msn.com/id/3032091/device/rss/io&#8221;));

    client = client.Finally(() => IsBusy = false);

    isBusy = true;

    client.Subscribe(item => rssItems.Add(item));

}

 

bool isBusy;

public bool IsBusy

{

    get { return isBusy; }

    private set

    {

        isBusy = value;

        if (PropertyChanged != null)

            PropertyChanged(this, new PropertyChangedEventArgs(“IsBusy”));

    }

}

Check out the vast library of Rx extension methods available to use. Getting an IObservable and pumping the results into an ObservableCollection is just the beginning of what you can do with a data client that returns an IObservable. Have fun and start using Rx!

See demo (Note: I threw in a very minor delay during deserialization of each item to exagerate the effect).

Download source

Read Full Post »