How to create a basic RSS reader for Windows Phone 8

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

You can create a basic RSS reader by completing the following steps. The guidance and code examples presented here are based on a code sample called RSS Reader Sample.

This topic contains the following sections.

Creating a Windows Phone app project

In Visual Studio, create a new Windows Phone App project.

Adding a reference to the Syndication DLL

To use the SyndicationFeed class, you must first add a DLL reference.

To add a reference to the Syndication DLL

  1. In Visual Studio, on the Project menu, select Add Reference, and then select the Browse tab.

  2. Navigate to your Program Files (x86) directory.

  3. Navigate to Microsoft SDKs/Silverlight/v4.0/Libraries/Client/.

  4. Select System.ServiceModel.Syndication.dll, and then click OK.

Note

If you see a prompt at this step that asks if you want to continue, click Yes.

Creating an RSS text trimmer

Description text in an RSS feed can often contain HTML, encoded characters, and other data that you may not want to display in an RSS reader. You can create a class that strips HTML and other unwanted data from description text, and then set up the class as a converter for the XAML field.

To create an RSS text trimmer

  1. In Solution Explorer, right-click the project name, click Add, and then click New Item.

  2. In the Installed Templates pane, select Visual C#, and then in the middle pane select Class.

  3. Name the file RssTextTrimmer.cs, and then click Add.

  4. In Solution Explorer, double-click the RssTextTrimmer.cs file.

  5. Add the following namespaces:

    using System.Windows.Data;
    using System.Globalization;
    using System.Text.RegularExpressions;
    
  6. Update the class definition so that it implements the IValueConverter interface:

    public class RssTextTrimmer : IValueConverter
    
  7. Add the following code inside the RssTextTrimmer class:

    // Clean up text fields from each SyndicationItem. 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return null;
    
        int maxLength = 200;
        int strLength = 0;
        string fixedString = "";
    
        // Remove HTML tags and newline characters from the text, and decode HTML encoded characters. 
        // This is a basic method. Additional code would be needed to more thoroughly  
        // remove certain elements, such as embedded Javascript. 
    
        // Remove HTML tags. 
        fixedString = Regex.Replace(value.ToString(), "<[^>]+>", string.Empty);
    
        // Remove newline characters.
        fixedString = fixedString.Replace("\r", "").Replace("\n", "");
    
        // Remove encoded HTML characters.
        fixedString = HttpUtility.HtmlDecode(fixedString);
    
        strLength = fixedString.ToString().Length;
    
        // Some feed management tools include an image tag in the Description field of an RSS feed, 
        // so even if the Description field (and thus, the Summary property) is not populated, it could still contain HTML. 
        // Due to this, after we strip tags from the string, we should return null if there is nothing left in the resulting string. 
        if (strLength == 0)
        {
            return null;
        }
    
        // Truncate the text if it is too long. 
        else if (strLength >= maxLength)
        {
            fixedString = fixedString.Substring(0, maxLength);
    
            // Unless we take the next step, the string truncation could occur in the middle of a word.
            // Using LastIndexOf we can find the last space character in the string and truncate there. 
            fixedString = fixedString.Substring(0, fixedString.LastIndexOf(" "));
        }
    
        fixedString += "...";
    
        return fixedString;
    }
    
    // This code sample does not use TwoWay binding, so we do not need to flesh out ConvertBack.  
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
    
  8. Set up the RssTextTrimmer class as a converter by double-clicking App.xaml in Solution Explorer, and then adding the following code inside the <Application.Resources> tag:

    <converter:RssTextTrimmer xmlns:converter="clr-namespace:namespace" x:Key="RssTextTrimmer" />
    

    Where namespace is the name of your project's namespace. For example, in the Basic RSS Reader for Windows Phone 8 sample, namespace is replaced with sdkBasicRSSReaderWP8CS.

Updating the XAML code

To update the XAML code

  1. In Solution Explorer, double-click the MainPage.xaml file.

  2. Replace <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"></Grid> with the following XAML code:

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,12,0">
        <Button Content="Load Feed" Height="72" HorizontalAlignment="Left" Margin="9,6,0,0" Name="loadFeedButton" VerticalAlignment="Top" Width="273" Click="loadFeedButton_Click" />
    
        <ListBox Name="feedListBox" Height="468" HorizontalAlignment="Left" Margin="20,100,0,0" VerticalAlignment="Top" Width="444" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionChanged="feedListBox_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel VerticalAlignment="Top">
                        <TextBlock TextDecorations="Underline" FontSize="24" Name="feedTitle" TextWrapping="Wrap" Margin="12,0,0,0" HorizontalAlignment="Left" Foreground="{StaticResource PhoneAccentBrush}" Text="{Binding Title.Text, Converter={StaticResource RssTextTrimmer}}" />
                        <TextBlock Name="feedSummary" TextWrapping="Wrap" Margin="12,0,0,0" Text="{Binding Summary.Text, Converter={StaticResource RssTextTrimmer}}" />
                        <TextBlock Name="feedPubDate" Foreground="{StaticResource PhoneSubtleBrush}" Margin="12,0,0,10" Text="{Binding PublishDate.DateTime}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Border BorderBrush="{StaticResource PhoneSubtleBrush}" BorderThickness="1" Height="2" HorizontalAlignment="Left" Margin="20,88,0,0" Name="border1" VerticalAlignment="Top" Width="438" />
    </Grid>
    

Updating the code-behind file

To update the code-behind file

  1. Add the following namespaces:

    using System.IO;
    using System.ServiceModel.Syndication;
    using System.Xml;
    using Microsoft.Phone.Tasks;
    
  2. Add the following code inside of the MainPage class, after the MainPage constructor.

Note

Basic tombstoning support is also included in the following code.

``` csharp
// Click handler that runs when the 'Load Feed' or 'Refresh Feed' button is clicked. 
private void loadFeedButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
    // WebClient is used instead of HttpWebRequest in this code sample because 
    // the implementation is simpler and easier to use, and we do not need to use 
    // advanced functionality that HttpWebRequest provides, such as the ability to send headers.
    WebClient webClient = new WebClient();

    // Subscribe to the DownloadStringCompleted event prior to downloading the RSS feed.
    webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);

    // Download the RSS feed. DownloadStringAsync was used instead of OpenStreamAsync because we do not need 
    // to leave a stream open, and we will not need to worry about closing the channel. 
    webClient.DownloadStringAsync(new System.Uri("https://windowsteamblog.com/windows_phone/b/windowsphone/rss.aspx"));
}

// Event handler which runs after the feed is fully downloaded.
private void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    if (e.Error != null)
    {
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            // Showing the exact error message is useful for debugging. In a finalized application, 
            // output a friendly and applicable string to the user instead. 
            MessageBox.Show(e.Error.Message);
        });
    }
    else
    {
        // Save the feed into the State property in case the application is tombstoned. 
        this.State["feed"] = e.Result;

        UpdateFeedList(e.Result);
    }
}

// This method determines whether the user has navigated to the application after the application was tombstoned.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    // First, check whether the feed is already saved in the page state.
    if (this.State.ContainsKey("feed"))
    {
        // Get the feed again only if the application was tombstoned, which means the ListBox will be empty.
        // This is because the OnNavigatedTo method is also called when navigating between pages in your application.
        // You would want to rebind only if your application was tombstoned and page state has been lost. 
        if (feedListBox.Items.Count == 0)
        {
            UpdateFeedList(State["feed"] as string);
        }
    }
}

// This method sets up the feed and binds it to our ListBox. 
private void UpdateFeedList(string feedXML)
{
    // Load the feed into a SyndicationFeed instance.
    StringReader stringReader = new StringReader(feedXML);
    XmlReader xmlReader = XmlReader.Create(stringReader);
    SyndicationFeed feed = SyndicationFeed.Load(xmlReader);

    // In Windows Phone OS 7.1 or later versions, WebClient events are raised on the same type of thread they were called upon. 
    // For example, if WebClient was run on a background thread, the event would be raised on the background thread. 
    // While WebClient can raise an event on the UI thread if called from the UI thread, a best practice is to always 
    // use the Dispatcher to update the UI. This keeps the UI thread free from heavy processing.
    Deployment.Current.Dispatcher.BeginInvoke(() =>
    {
        // Bind the list of SyndicationItems to our ListBox.
        feedListBox.ItemsSource = feed.Items;

        loadFeedButton.Content = "Refresh Feed";
    });
}

// The SelectionChanged handler for the feed items 
private void feedListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ListBox listBox = sender as ListBox;

    if (listBox != null && listBox.SelectedItem != null)
    {
        // Get the SyndicationItem that was tapped.
        SyndicationItem sItem = (SyndicationItem)listBox.SelectedItem;

        // Set up the page navigation only if a link actually exists in the feed item.
        if (sItem.Links.Count > 0)
        {
            // Get the associated URI of the feed item.
            Uri uri = sItem.Links.FirstOrDefault().Uri;

            // Create a new WebBrowserTask Launcher to navigate to the feed item. 
            // An alternative solution would be to use a WebBrowser control, but WebBrowserTask is simpler to use. 
            WebBrowserTask webBrowserTask = new WebBrowserTask();
            webBrowserTask.Uri = uri;
            webBrowserTask.Show();
        }
    }
}
```

See Also

Other Resources

Communications for Windows Phone 8