Mobile Zone is brought to you in partnership with:

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

Building a RSS reader for Windows Phone 7 – Developing the functionality

08.06.2010
| 26597 views |
  • submit to reddit

UPDATE: If you are experiencing feed problems caused by an invalid date, make sure you read this post and download the updated project.

In my previous article I showed the way I designed the basic UI and logical skeleton for the feed reader. In this article I am going to show how the code is organized that makes this application work the way it should. You need to read the previous article in order to apply the methods shown below.

Adding and removing feed URLs

As you know, the application supports multiple feeds. Therefore, the content is aggregated from more than one feed at once. So, in the ManageFeeds page, you should add an event handler for the Add button that will allow the user to add multiple URLs. If you look at the existing XAML markup, the event handler is already declared in the element markup, so all you have to do is actually implement it:

private void Button_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(urlHolder.Text.Trim()))
{
if (!App.Data.FeedList.Contains(urlHolder.Text))
{
if (Uri.IsWellFormedUriString(urlHolder.Text, UriKind.Absolute))
{
App.Data.FeedList.Add(urlHolder.Text);
urlHolder.Text = string.Empty;
}
}
}
}

I am verifying if the URL passed is well-formatted (this will avoid issues with invalid URLs) via Uri.IsWellFormattedUriString. I thought about using a web request to check whether the URL works, but since it is a phone, this could be too expensive, traffic-wise.

Since FeedList is an ObservableCollection and it is bound to the ListBox that contains the URLs, the ListBox will re-bind automatically and you will see the item added to the list without additional code.

For that reason, item removal is tied only to removing the actual item from the list.

private void Remove_Click(object sender, RoutedEventArgs e)
{
if (MainList.SelectedItem != null)
{
App.Data.FeedList.Remove(MainList.SelectedItem.ToString());
}
}

Preserving feed URLs for later re-use

This is done by implementing a Save method inside the App class:

void SaveList()
{
IsolatedStorageSettings.ApplicationSettings.Clear();

if (App.Data.FeedList.Count != 0)
{
foreach (string item in App.Data.FeedList)
{
IsolatedStorageSettings.ApplicationSettings.Add(item, item);
}
}

IsolatedStorageSettings.ApplicationSettings.Save();
}

This will update the application settings with the items currently present in the collection. Later on, this method should be called from Application_Closing (triggered when the application is closed) and Application_Deactivated (triggered when the application is sent to the background).

// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
SaveList();
}

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
SaveList();
}

Getting feed contents

I was surprised by this, but by default I could not access System.ServiceModel.Syndication. Even when trying to add a reference, the library wasn’t on the list. The solution to this is simple – all you have to do is use the same Add Reference dialog, navigate to <SystemDrive>:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client and select System.ServiceModel.Syndication.dll:

Once done, in my FeedHelper class I am creating a GetItems() method:

public static void GetItems()
{
App.Model.FeedItems.Clear();

foreach (string item in App.Data.FeedList)
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri(item));
}
}

First of all, I am clearing the existing feed items cache and then initializing a WebClient for each registered feed. A separate WebClient instance is created for each feed because one instance cannot handle multiple downloads at once. As you see, this method is bound to DownloadStringCompletedEventHandler for the WebClient instance that is used inside of it. The event handler looks like this:

static void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Result))
{
XmlReader reader = XmlReader.Create(new StringReader(e.Result));
SyndicationFeed feed = SyndicationFeed.Load(reader);

foreach (SyndicationItem sItem in feed.Items)
{
if ((sItem != null) && (sItem.Summary != null) && (sItem.Title != null))
{
App.Model.FeedItems.Add(
new ViewModel.ItemModel()
{
ItemDetails = sItem.Summary.Text,
ItemTitle = sItem.Title.Text
}
);
}
}
}
}

Here I am checking whether there is a result for the URL used for feed content retrieval. If there is, then I am creating a XmlReader instance that will get the contents in XML format and then passing it to a SyndicationFeed instance that makes it much easier to handle RSS feeds. If the item is not null (and so are its main used properties), then a new instance of ItemModel is created for each item and added to the item collection.

NOTE: To avoid NullReference exceptions, in the App constructor add the following line:

Model.FeedItems = new ObservableCollection<ViewModel.ItemModel>();

In the same ManageFeeds page, I am overriding the OnNavigatedFrom method:

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
FeedHelper.FeedData.GetItems();
base.OnNavigatedFrom(e);
}

This way, the feed contents are updated when the user navigates away from the feed list.

On the main page, for the ‘reload feeds’ menu item in the application bar, you need to implement the same functionality. Since there is an event handler declared in XAML, all you need is to provide a method call in the code-behind:

private void reloadFeeds_Click(object sender, EventArgs e)
{
FeedHelper.FeedData.GetItems();
}

If you launch the application and add a few valid feeds, then when the list is reloaded, you will see the content aggregated there.

Opening posts

Now, the application seems to be useful, but not to the extent it could be. Each syndication item is pointing to an actual published post. So why not open that post in a new page?

To do this, add a new Portrait Page and name it DetailsView.xaml.

Now, you need to modify the existing ItemModel class to include one more property –ItemLink, that will store an assigned post link. The property along with the associated private field should look like this:

private string itemLink;
public string ItemLink
{
get { return itemLink; }
set
{
if (value != itemLink)
{
itemLink = value;
NotifyPropertyChanged("ItemLink");
}
}
}

Add it to ItemModel and once done, go to the client_DownloadStringCompleted event handler (n FeedHelper) and modify the added instance of ItemModel to add one more property – ItemLink:

App.Model.FeedItems.Add(
new ViewModel.ItemModel()
{
ItemDetails = sItem.Summary.Text,
ItemTitle = sItem.Title.Text,
ItemLink=sItem.Links[0].Uri.ToString()
}
);

Now, on the page that you recently created (DetailsView), inside the internal grid, add a WebBrowser control. My grid markup looks like this:

<Grid x:Name="LayoutRoot" Background="Transparent">   
<phone:WebBrowser Name="webBrowser1"/>
</Grid>

All good by this point, but there is an event handler needed for the ListBox on the main page, so that when one item is clicked, it navigates to the page with the web browser and opens the assigned page. In order to do this, make sure that the ListBox on the main page has an associated SelectionChanged event handler:

<ListBox x:Name="MainList" ItemsSource="{Binding FeedItems}" Margin="13,0,13,0" SelectionChanged="ListBox_SelectionChanged">

The actual event handler:

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (MainList.SelectedItems.Count != 0)
{
NavigationService.Navigate(new Uri("/DetailsView.xaml?item=" + MainList.SelectedIndex, UriKind.Relative));
}
}

I am passing a parameter with the currently selected item index, so on the page with the web browser I need to read this parameter and get the URL from the item collection. I can do this simply by using the Loaded event handler for that page:

void DetailsView_Loaded(object sender, RoutedEventArgs e)
{
string index = "";
if (NavigationContext.QueryString.TryGetValue("item", out index))
{
int _index = int.Parse(index);
webBrowser1.Navigate(new Uri(App.Model.FeedItems[_index].ItemLink));
}
}

And that is it. Once you launch the application, reload the feeds and click on any item. You will see the details page open and the associated link navigated to in the WebBrowser control:

If you want to download the project, you can do so by clicking here [ZIP file – Visual Studio 2010 solution]. Of course, Windows Phone 7 Development Tools are required.

Comments

Ravindra Soni replied on Tue, 2010/09/14 - 9:31am

Thanks Dennis fro this post.Helped a lot to catch with the WP7. I ran this app on machine successfully at the first try. But after a while id stop responding. To be specific, when I click the "+" (add feed) button under "manage feeds", it is not responding at all. I even tried the same code on different machine.But didn't help at all. Can you help? Thanks.

Ravindra Soni replied on Tue, 2010/09/14 - 9:31am

Thanks Dennis for this post.Helped a lot to catch with the WP7. I ran this app on machine successfully at the first try. But after a while id stop responding. To be specific, when I click the "+" (add feed) button under "manage feeds", it is not responding at all. I even tried the same code on different machine.But didn't help at all. Can you help? Thanks.

Den D. replied on Wed, 2010/09/15 - 5:03pm

Try running through a couple of breakpoints and see where it's getting stuck - that's the first thing you can (and should) do. Specifically for the event handler that is tied to the button. So far, it is working for me.

Jesse Mccain replied on Sat, 2010/09/25 - 2:10am

How would I be able to add the option to automatically display RSS Feed from a source without having them to add it ?

Thanks!

Den D. replied on Tue, 2010/09/28 - 10:29pm

Well you need to add a feed source anyway - how would you otherwise specify the feed URL?

Correct me if I didn't understand your question the right way.

Jesse Mccain replied on Wed, 2010/09/29 - 10:55am

Well I'm trying to display the Feed source automatically in the screen like the feed list page instead of just giving my clients the option to enter the feed themselfs.

Thanks!

Den D. replied on Wed, 2010/09/29 - 8:38pm

All you have to do then is store the feed source in code and then access it directly - eliminate the page that is handling the feed URLs.

Jesse Mccain replied on Wed, 2010/09/29 - 10:09pm

How do I go about that if its not more than 2-3 lines if you can please help me out if its more I don't want to waste your time.

Peter Live replied on Sun, 2010/10/24 - 3:26pm

Hi Den,

Thank you for these great posts. They are great help to undrestand Windows Phone 7 programming.

 Can you please have one tutorial for this situation:

 1- We read a feed with these 3 fields: Title, Description, Link (We provide RSS link in code and user doesn't need to enter any link).

On our main page we just show Title and Description of feed as ListBox. The Link field for each feed is URL to another(xml) file which has much more detail about that feed or news and let say that XML file has 2 fields itself: Title, Description.

 2- When user click on one of the feeds on main page it pass the link to the new page. In new page we read the feed with 2 fields and simply just show it like any regular rss reader.

Basically there is one main RSS and each feed on that RSS refer toit's own specific xml file and when user click on each feed it reads that xml file and show it in new page.

Thanks!

Veeru Gullanki replied on Tue, 2010/11/30 - 7:22am

Hi, I am unable to download the source from the following url http://dotnet.dzone.com/articles/building-rss-reader-windows-0 when I have download and unzip, it is says, there are no files in zip file. please suggest me, how to download the source code. Thanks, veeru.

Den D. replied on Tue, 2010/11/30 - 6:32pm

I can download and unzip it just fine (using WinRAR). What tool are you using to open the ZIP file?

Marsel Preci replied on Fri, 2011/01/14 - 7:53am

Hi Dennis,

First of all, I want to thank you for this great job and all this explanation about rss reader on wp7. I have tested the app and now I’m studding it. I'm a sudent and I’m really new in c# and of course in wp7. I have a question. If I want to save in isolate storage some favorite items (title, description and link) what do I have to do? I really appreciate every kind of help that you can give. Thank you

Marsel

Den D. replied on Sun, 2011/01/16 - 1:58am

Marsel, I would highly recommend watching this video for your situation.

Nikola Cvetkovic replied on Sun, 2011/03/27 - 3:38pm

Hy Den, can you tell something about showing images in listbox, after reading news, on feed list page, of the app, because it doesn't show after reading some rss sources? thanks

Lance Seidman replied on Tue, 2012/02/14 - 3:45am in response to: Jesse Mccain

Put this in your Main:

 

txtBoxName.Text = "URL-TO-FEED";

but if you want them to allow to change the textbox only otherwise re-write the code to not look for the textbox and simply define a string that's the URL for it to constantly choose (this way if you ever need to update the URI it's in one spot).

Lance Seidman replied on Tue, 2012/02/14 - 3:48am in response to: Nikola Cvetkovic

 

<Image Source="{Binding img-url}" Visibility="Visible"/> 

Simply use (in the xaml) for the listbox item (image control) source: {Binding <img-url>}...

Carla Brian replied on Sun, 2012/07/08 - 1:35am

This is really nice. Good to see about this one. This could really be useful. Good job on this. - Mercy Ministries

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.