Reading RSS items on Windows Phone 7

3 בפברואר 2011

6 תגובות

Following is a utility class I wrote to help me read RSS items asynchronously on a Silverlight for Windows Phone 7 application.

I will present how it was written and then how to use it. At the end of this post you can find a sample application that contains all the code.

How to read RSS?

Add Helper Assembly

The first thing we need to do is to add a reference to the assembly, System.ServiceModel.Syndication.dll which contains classes that can parse RSS feeds.

This assembly is part of the Silverlight 3 SDK. It isn’t part of the Windows Phone 7 SDK, but there is no problem in using it there. You can find the file under:
%ProgramFiles%\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.ServiceModel.Syndication.dll

Add Reference dialog

You will get the following warning, just ignore it, it works.

Adding a reference to a Silverlight assembly may lead to unexpected application behavior. Do you want to continue?

Define RssItem

Before we get to business, let’s define a model class that will encapsulate the data of a single RSS item.

Our RssItem class will hold the following properties: Title, Summary, PublishedDate and Url. In addition I provide a PlainSummary property that will hold a plain text version of the RSS item. Usually the summary has HTML tags that we just want to ignore, thus PlainSummary for the rescue.

/// <summary>
/// Model for RSS item
/// </summary>
public class RssItem
{
    /// <summary>
    /// Initializes a new instance of the <see cref="RssItem"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    /// <param name="summary">The summary.</param>
    /// <param name="publishedDate">The published date.</param>
    /// <param name="url">The URL.</param>
    public RssItem(string title, string summary, string publishedDate, string url)
    {
        Title = title;
        Summary = summary;
        PublishedDate = publishedDate;
        Url = url;

        // Get plain text from html
        PlainSummary = HttpUtility.HtmlDecode(Regex.Replace(summary, "<[^>]+?>", ""));
    }

    /// <summary>
    /// Gets or sets the title.
    /// </summary>
    /// <value>The title.</value>
    public string Title { get; set; }

    /// <summary>
    /// Gets or sets the summary.
    /// </summary>
    /// <value>The summary.</value>
    public string Summary { get; set; }

    /// <summary>
    /// Gets or sets the published date.
    /// </summary>
    /// <value>The published date.</value>
    public string PublishedDate { get; set; }

    /// <summary>
    /// Gets or sets the URL.
    /// </summary>
    /// <value>The URL.</value>
    public string Url { get; set; }

    /// <summary>
    /// Gets or sets the plain summary.
    /// </summary>
    /// <value>The plain summary.</value>
    public string PlainSummary { get; set; }
}

Implement The RSS service

The RSS service will get the RSS feed URL as a parameter.
In addition, since we want our class to work asynchronously we will accept as parameters a few more delegates:

  • Action<IEnumerable<RssItem>> onGetRssItemsCompleted, which will be called when the RSS items are ready to be processed.
  • Action<Exception> onError, which will be called if there is an error while getting the RSS items.
  • Action onFinally, which will be called always, whether there was an exception or not. Think of it as the finally section a common try-catch block.

So the method signature will be:

public static void GetRssItems(string rssFeed, Action<IEnumerable<RssItem>> onGetRssItemsCompleted = null, Action<Exception> onError = null, Action onFinally = null)

To get the feed XML we will use the WebClient class:

WebClient webClient = new WebClient();

// register on download complete event
webClient.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
{
    …
};

webClient.OpenReadAsync(new Uri(rssFeed));

The actual RSS parsing is done using the SyndicationFeed class from our helper assembly:

// convert rss result to model
List<RssItem> rssItems = new List<RssItem>();
Stream stream = e.Result;
XmlReader response = XmlReader.Create(stream);
SyndicationFeed feeds = SyndicationFeed.Load(response);
foreach (SyndicationItem f in feeds.Items)
{
    RssItem rssItem = new RssItem(f.Title.Text, f.Summary.Text, f.PublishDate.ToString(), f.Links[0].Uri.AbsoluteUri);
    rssItems.Add(rssItem);
}

The rest of the code is for handling the different delegates: onCompleted, onError and onFinally. I bring here the method in its full:

/// <summary>
/// Gets the RSS items.
/// </summary>
/// <param name="rssFeed">The RSS feed.</param>
/// <param name="onGetRssItemsCompleted">The on get RSS items completed.</param>
/// <param name="onError">The on error.</param>
public static void GetRssItems(string rssFeed, Action<IEnumerable<RssItem>> onGetRssItemsCompleted = null, Action<Exception> onError = null, Action onFinally = null)
{
    WebClient webClient = new WebClient();

    // register on download complete event
    webClient.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
    {
        try
        {
            // report error
            if (e.Error != null)
            {
                if (onError != null)
                {
                    onError(e.Error);
                }
                return;
            }

            // convert rss result to model
            List<RssItem> rssItems = new List<RssItem>();
            Stream stream = e.Result;
            XmlReader response = XmlReader.Create(stream);
            SyndicationFeed feeds = SyndicationFeed.Load(response);
            foreach (SyndicationItem f in feeds.Items)
            {
                RssItem rssItem = new RssItem(f.Title.Text, f.Summary.Text, f.PublishDate.ToString(), f.Links[0].Uri.AbsoluteUri);
                rssItems.Add(rssItem);
            }

            // notify completed callback
            if (onGetRssItemsCompleted != null)
            {
                onGetRssItemsCompleted(rssItems);
            }
        }
        finally
        {
            // notify finally callback
            if (onFinally != null)
            {
                onFinally();
            }
        }
    };

    webClient.OpenReadAsync(new Uri(rssFeed));
}

Using the RSS Service

Using the class is very easy, just provide the URL as the first parameter and a delegate for getting the “completed” notification.

private void Button_Click(object sender, RoutedEventArgs e)
{
    RssService.GetRssItems(
        WindowsPhoneBlogPosts,
        (items) => { listbox.ItemsSource = items; },
        (exception) => { MessageBox.Show(exception.Message); },
        null
        );
}

There is a sample application which can be downloaded here.

image

 

Note: this code was first published as part of the “Using Pivot and Panorama Controls” lab found in the Windows Phone Training Kit for Developers, which I wrote for Microsoft.

That’s it for now,
Arik Poznanski.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

6 תגובות

  1. Dimitris Gkanatsios7 בפברואר 2011 ב 3:28

    It's better to use HttpWebRequest/HttpWebResponse rather than WebClient. In WebClient, the …Completed event is raised on the UI thread, contrary to what's happening in HttpWebRequest.

    להגיב
  2. kamilex1 באפריל 2011 ב 17:09

    Very nice blog. I was added to bookmark this post. Keep good work.

    להגיב
  3. kamilex1 באפריל 2011 ב 17:17

    Very nice blog. I was added to bookmark this post. Keep good work.

    להגיב
  4. technotim15 ביולי 2011 ב 18:40

    HI thanks for this. How can I make the title's actual URLs so that I can read more? Also is there an easy way to remove the button so it grabs the feeds on load rather than clicking the button?

    להגיב
  5. arik17 ביולי 2011 ב 18:15

    check out the lab named “Using Pivot and Panorama Controls” which can be found in the Windows Phone Training Kit for Developers, in the following address:

    http://msdn.microsoft.com/en-us/WP7TrainingCourse_UsingPivotAndPanoramaControls

    specifically, you can use HyperlinkButton to make links to the URLs.
    To auto load the content simply move the code to the load event of the page instead of button clicked.

    להגיב
  6. Gianluca10 ביולי 2012 ב 15:54

    Great blog!

    But I have the same question as 'technotim':
    How do I get the URL from the selected Item to handle it (open in a web-browser)?

    Thanks
    Gianluca

    להגיב