Windows Phone 7 Series development: reading RSS feeds
One limitation on Windows Phone 7 is related to System.Net namespace classes. There is no convenient way to read data from web. There is no WebClient class. There is no GetResponse() method – we have to do it all asynchronously because compact framework has limited set of classes we can use in our applications to communicate with internet. In this posting I will show you how to read RSS-feeds on Windows Phone 7.
NB! This is my draft code and it may contain some design flaws and some questionable solutions. This code is intended to use as test-drive for Windows Phone 7 CTP developer tools and I don’t suppose you are going to use this code in production environment.
Current state of my RSS-reader
Currently my RSS-reader for Windows Phone 7 is very simple, primitive and uses almost all defaults that come out-of-box with Windows Phone 7 CTP developer tools.
My first goal before going on with nicer user interface design was making RSS-reading work because instead of convenient classes from .NET Framework we have to use very limited classes from .NET Framework CE. This is why I took the reading of RSS-feeds as my first task.
There are currently more things to solve regarding user-interface. As I am pretty new to all this Silverlight stuff I am not very sure if I can modify default controls easily or should I write my own controls that have better look and that work faster.
The image on right shows you how my RSS-reader looks like right now. Upper side of screen is filled with list that shows headlines from this blog. The bottom part of screen is used to show description of selected posting. You can click on the image to see it in original size.
In my next posting I will show you some improvements of my RSS-reader user interface that make it look nicer. But currently it is nice enough to make sure that RSS-feeds are read correctly.
FeedItem class
As this is most straight-forward part of the following code I will show you RSS-feed items class first. I think we have to stop on it because it is simple one.
public class FeedItem
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime PublishDate { get; set; }
public List<string> Categories { get; set; }
public string Link { get; set; }
public FeedItem()
{
Categories = new List<string>();
}
}
RssClient
RssClient takes feed URL and when asked it loads all items from feed and gives them back to caller through ItemsReceived event. Why it works this way? Because we can make responses only using asynchronous methods. I will show you in next section how to use this class.
Although the code here is not very good but it works like expected. I will refactor this code later because it needs some more efforts and investigating. But let’s hope I find excellent solution. :)
public class RssClient
{
private readonly string _rssUrl;
public delegate void ItemsReceivedDelegate(RssClient client, IList<FeedItem> items);
public event ItemsReceivedDelegate ItemsReceived;
public RssClient(string rssUrl)
{
_rssUrl = rssUrl;
}
public void LoadItems()
{
var request = (HttpWebRequest)WebRequest.Create(_rssUrl);
var result = (IAsyncResult)request.BeginGetResponse(ResponseCallback, request);
}
void ResponseCallback(IAsyncResult result)
{
var request = (HttpWebRequest)result.AsyncState;
var response = request.EndGetResponse(result);
var stream = response.GetResponseStream();
var reader = XmlReader.Create(stream);
var items = new List<FeedItem>(50);
FeedItem item = null;
var pointerMoved = false;
while (!reader.EOF)
{
if (pointerMoved)
{
pointerMoved = false;
}
else
{
if (!reader.Read())
break;
}
var nodeName = reader.Name;
var nodeType = reader.NodeType;
if (nodeName == "item")
{
if (nodeType == XmlNodeType.Element)
item = new FeedItem();
else if (nodeType == XmlNodeType.EndElement)
if (item != null)
{
items.Add(item);
item = null;
}
continue;
}
if (nodeType != XmlNodeType.Element)
continue;
if (item == null)
continue;
reader.MoveToContent();
var nodeValue = reader.ReadElementContentAsString();
// we just moved internal pointer
pointerMoved = true;
if (nodeName == "title")
item.Title = nodeValue;
else if (nodeName == "description")
item.Description = Regex.Replace(nodeValue,@"<(.|\n)*?>",string.Empty);
else if (nodeName == "feedburner:origLink")
item.Link = nodeValue;
else if (nodeName == "pubDate")
{
if (!string.IsNullOrEmpty(nodeValue))
item.PublishDate = DateTime.Parse(nodeValue);
}
else if (nodeName == "category")
item.Categories.Add(nodeValue);
}
if (ItemsReceived != null)
ItemsReceived(this, items);
}
}
This method is pretty long but it works. Now let’s try to use it in Windows Phone 7 application.
Using RssClient
And this is the fragment of code behing the main page of my application start screen. You can see how RssClient is initialized and how items are bound to list that shows them.
public MainPage()
{
InitializeComponent();
SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
listBox1.Width = Width;
var rssClient = new RssClient("http://feedproxy.google.com/gunnarpeipman");
rssClient.ItemsReceived += new RssClient.ItemsReceivedDelegate(rssClient_ItemsReceived);
rssClient.LoadItems();
}
void rssClient_ItemsReceived(RssClient client, IList<FeedItem> items)
{
Dispatcher.BeginInvoke(delegate()
{
listBox1.ItemsSource = items;
});
}
Conclusion
As you can see it was not very hard task to read RSS-feed and populate list with feed entries. Although we are not able to use more powerful classes that are part of full version on .NET Framework we can still live with limited set of classes that .NET Framework CE provides.