Silverlight Feedbooks RSS Browser - Part 1
Tutorial - Creating a Simple Silverlight RSS Reader
Introduction
This is the first post of a multipart blog tutorial explaining how to create a simple Silverlight 2.0 Web Application that displays a grid of thumbnails from a Feedbooks RSS source. Under each thumbnail is a button that allows users to download an ePub version of the book to their desktop.
Download Source See also: Tutorial Part 2
Feedbooks RSS Browser
In this first part we are going to step through the process of fetching and parsing an RSS feed with Silverlight and then bind the RSS data to a Silverlight DataGrid control.
In this Article we build the following application:
This Tutorial: Feedbooks RSS DataGrid
See it in ActionDownload Source
Subsequent tutorials in this series will expand upon the concepts below on our way to the final Feedbooks Browser application.
A Little Background
Feedbooks.com is an interesting web resource that allows readers to access free electronic versions of books in the public domain. It is a great site for anyone that enjoys reading.
An epub file refers to an electronic book created according to the IDPF ePub Standard. Epub files are viewable with such applications as the free Adobe Digital Editions.
Microsoft Silverlight is a cross-platform web application framework similar in many ways to Adobe’s Flash Platform. Silverlight enables web pages to contain animations, multimedia and Rich Internet Applications.
This article is targeted at developers that are new to the Silverlight framework and that have Visual Studio 2008 SP1 with the Silverlight Toolkit. This article makes use of C# for its programming examples.
Setup
The first step is to create a new C# Silverlight Application Project for our Feedbooks Browser in Visual Studio. Creating a new Silverlight Application should create 2 projects in our solution:
- A C# projects with App.xaml, Page.xaml and their respective code-behinds
- And a Web project with our .html files, ClientBin, etc.
The Web project is an important aspect of this example as it allows us to access remote RSS feeds without tripping Silverlight Security Errors when we test and debug.
Now that we have our blank project framework we can look at the steps necessary to fetch the Feedbooks RSS feed.
Fetching the RSS Feed
Start by opening the C# code behind file for the Page.xaml file, called Page.xaml.cs
Page.xaml.cs is a class that extends UserControl so that it can control the UI Elements defined in the default Page object (defined by the Page.xaml file). This is similar to creating code behind files for MXML Adobe Flex projects. Our first edit will be to add a constant string that holds the URI of the Feedbooks RSS Feed. Our second edit will be to call a method, GetFeedbooksFeed, that will fetch the RSS feed for us from the constructor of the Page object.
private const string rssFeedbooksUri = “http://feedbooks.com/discover/list.rss?order=top&range=week”;
public Page()
{
InitializeComponent();
GetFeedbooksFeed();
}
GetFeedbooksFeed() will use a WebClient to fetch the RSS. Since this is an asynchronous action we will need to add an event handler to the mix that will be called when the data arrives. In this case we will use a DownloadStringCompletedEventHandler.
private void GetFeedbooksFeed()
{
//create a WebClient to read feedbooks RSS feed
WebClient rssFeedbookService = new WebClient();
rssFeedbookService.DownloadStringCompleted += new DownloadStringCompletedEventHandler(RssFeedbook_Completed);
rssFeedbookService.DownloadStringAsync(new Uri(rssFeedbooksUri, UriKind.RelativeOrAbsolute));
}
In our DownloadStringCompletedEventHandler event handler we will test the returned event arguments for errors to insure that our action completed successfully. If there are errors, we want write them to the Output Window using Debug.Writeline(). But before we can call this method we must add a using directive to the top of our class that points to the package in which Debug.Writeline() lives.
…
using System.Windows.Shapes;
using System.Diagnostics;
We can then add our event handler below the GetFeedbooksFeed method
private void RssFeedbook_Completed(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)\
{
//turn rss feed into UI elements
DisplayFeedBooks(e.Result);
}
else
{
//write any errors to output window
Debug.WriteLine(”ERROR ” + e.Error);
}
}
Now that we have our RSS XML data and have tested for errors, it is time to turn the data into a user interface.
Adding the LINQ Assembly Reference
In order to parse our RSS feed, we will make use of an interesting .NET/Silverlight 2.0 feature called LINQ. LINQ stands for Language Integrated Query which extends the C# and VisualBasic languages to allow for SQL-type queries to be made on objects in a data set. When used with XML, LINQ allows us to very easily parse objects from the XML DOM.
The classes that make up LINQ reside in the System.Xml.Linq name space. But in order to add this namespace to our project we will have to add the Assembly that defines them as a Resource to our project.
This is accomplished by right clicking on the References folder in the Solution Explorer for our project and choosing Add Reference…
You should then be able to find and add the System.Xml.Linq Assembly from the .NET tab of the Add Reference Dialog. Once this Assembly is added we can add a using directive to the top of our class for LINQ.
using System.Xml.Linq;
Now that we are able to use the objects in the Linq namespace we are ready to parse our RSS.
Parsing the RSS Feed with LINQ
Before we parse our RSS feed we need to create a class to hold the book objects that are represented in the RSS’ XML data.
public class FeedbooksBook
{
private XElement _BookXml;
//public getter/setter values
public string Title { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public string Guid { get; set; }
public string ThumbUrl { get; set; }
//all public members are set by BookXml setter
public XElement BookXml
{
set
{
//first url decode the book xml
string BookStr = HttpUtility.HtmlDecode(value.ToString());
BookStr = HttpUtility.HtmlDecode(BookStr);
//parse xml
_BookXml = XElement.Parse(BookStr);
//set public string values from book xml
Title = _BookXml.Element(”title”).Value;
Description = _BookXml.Element(”description”).ToString();
Link = _BookXml.Element(”link”).Value;
Guid = _BookXml.Element(”guid”).Value;
//derive the thumb url from the description field
XElement ThumbXml = _BookXml.Element(”description”).Element(”img”);
ThumbUrl = ThumbXml.Attribute(”src”).Value;
}
get { return _BookXml; }
}
}
Note in the above class the new C# shorthand syntax for declaring getter/setters and the fact that all of the other public members are set in the setter for the BookXML property; in this way we can encapsulate all the xml parsing logic into one spot. Also of note is the fact that we HtmlDecode the xml before parsing it so that special chars in the XML are handled correctly. HtmlDecode is in the Systems.Window.Browser namespace and this requires us to add another using directive to the top of our class.
using System.Windows.Browser;
Getting back to our RSS feed we can now define the method that uses LINQ to parse the objects from the XML.
void DisplayFeedBooks(string xmlContent)
{
//parse the rss feed as an XDocument
XDocument channel = XDocument.Parse(xmlContent);
//use Linq to create FeedbooksBook Value Objs
IEnumerable<FeedbooksBook> BooksList =
from item in channel.Descendants(”item”)
where item.Element(”title”) != null
select new FeedbooksBook
{
BookXml = item,
};
//set the datagrid to use the books list
dgFeedbooks.ItemsSource = BooksList;
}
This method makes use of a query that creates FeedbooksBooks objects and places them in an enumerated structure called BookList. BookList can then be bound to a datagrid that is defined in XAML in the Page.xaml file.
Creating a DataGrid UI Control on XAML
The final step in this tutorial is to add the DataGrid UI Control to the Page.xaml file. The DataGrid control resides in the Assembly System.Windows.Controls.Data. You will need to add this Assembly as a reference like we did for LINQ earlier.
Once the Assembly Refernce is added to our project we can define the namespace, data, for it in the UserControl tag.
<UserControl x:Class=”FeedbooksBrowser.Page”
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:data=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data“>
We can then add our DataGrid control to the page XAML:
<Grid x:Name=”LayoutRoot” Background=”White”>
<Grid.RowDefinitions>
<RowDefinition Height=”28″/>
<RowDefinition Height=”*”/>
<RowDefinition Height=”40″/>
</Grid.RowDefinitions>
<!– Top Bar –>
<StackPanel Grid.Row=”0″ Orientation=”Horizontal” VerticalAlignment=”Center” Height=”28″ Background=”#FF639acf”>
<TextBlock x:Name=”txtHeader” Text=”Feedbooks Browser” FontSize=”12″ Foreground=”#FFffffff” Margin=”10,4,0,0″/>
</StackPanel>
<!– Data Grid–>
<data:DataGrid Grid.Row=”1″ x:Name=”dgFeedbooks” AutoGenerateColumns=”True”/>
<!– Bottom Bar –>
<StackPanel Grid.Row=”2″ Orientation=”Horizontal” VerticalAlignment=”Center” Height=”40″ Background=”#FF639acf”/>
</Grid>
</UserControl>
Since the DataGrid uses AutoGenerateColumns, all the public members of the FeedbooksBook class, which reside in the Enumerated list we bound to the DataGrid’s ItemsSource property, will be added as columns of the DataGrid via their ToString() method.
In our next post we will explore moving our User Interface from a simple Datagrid to a more aesthetically pleasing grid of thumbnails.
-Patrick
Patrick Keating is the Technical director at Seattle-based interactive agency Bluefire Productions.
January 22nd, 2009 at 1:28 am
Hi Patrick
I had an issue with the Silverlight WebClient class, in that a security error is thrown if you are inside a corporate network and your browser uses a proxy server address to connect to the internet.(in IE, tools > Internet options > Connections > LAN Connection). You’ll notice that because Silverlight is a cut down version of the CLR, there is no ‘Proxy’ property you can set on the WebClient class.
The work around: instead of using the Silverlight WebClient class, I had to create a WCF service in the hosting ASP.NET website and use the normal WebClient class in System.Net, which enabled me to set the Proxy address:
[OperationContract]
public string GetRssFeedPosts(string url, string proxyAddress) {
WebClient client = new WebClient();
if(!String.IsNullOrEmpty(proxyAddress))
client.Proxy = new WebProxy(proxyAddress);
return client.DownloadString(url);
}
Phil
January 22nd, 2009 at 11:24 am
Thank you for this comment, Phil. Any good Silverlight data is appreciated! I will check out the WCF services for future posts. -Patrick.