Reading and Displaying RSS Feed in a Silverlight DataGrid
The SyndicationFeed class in Silverlight 2 makes it easy to parse the feed response. The process of reading a feed is as simple as reading a RSS/ATOM feed using the WebClient class, load the stream into the SyndicationFeed class and bind it to a Silverlight UI. Let us see how to read and display feed in a Silverlight control:
Step 1: Open Visual Studio 2008 > File > New Project > Select the language (C# or VB.NET) > Select ‘Silverlight’ in the Project Types > from the templates, select ‘Silverlight Application’.
Step 2: Type a name (ConsumingRSS) and location for the project> click OK.
Choose the first option ‘Add a new ASP.NET Web project to the solution to host Silverlight’ and the project type as ‘ASP.NET Web Site’ and click OK. You will see that two projects are created: ConsumingRSS.Web and ConsumingRSS.
Step 3: Right click ConsumingRSS project> Add Reference > .NET Tab > Add a reference to System.ServiceModel.Syndication. In the Page.xaml.cs or vb add the following namespaces:
C#
using System.ServiceModel.Syndication;
using System.IO;
using System.Collections.Generic;
using System.Linq;
VB.NET
Imports System.ServiceModel.Syndication
Imports System.IO
Imports System.Collections.Generic
Imports System.Linq
Now drag and drop a TextBox, a Button and a DataGrid control from the toolbox to the Page.xaml. After rearranging these controls in the Grid, the markup will look similar to the following:
<UserControl xmlns:data="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Data" x:Class="ConsumingRSS.Page"
xmlns="http://schemas.microsoft.com/winfx/
2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox x:Name="txtFeedLoc" Width="450" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" ></TextBox>
<Button x:Name="btnFetch" Grid.Row="0" Content="Fetch RSS" Width="100" HorizontalAlignment="Left" Grid.Column="1" Click="btnFetch_Click"></Button>
<data:DataGrid x:Name="dGrid" AutoGenerateColumns="False" IsReadOnly="True" CanUserSortColumns="True" Grid.Row="1" Grid.ColumnSpan="2">
</data:DataGrid>
</Grid>
</UserControl>
If you observe, a click handler for the Button has been created. On the click event, we read the Feed URI from the textbox. The entire code is listed below:
C#
using System;
using System.Xml;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.ServiceModel.Syndication;
using System.IO;
using System.Collections.Generic;
using System.Linq;
namespace ConsumingRSS
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
protected void LoadRSS(string uri)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted +=new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
Uri feedUri = new Uri(uri, UriKind.Absolute);
wc.OpenReadAsync(feedUri);
}
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
txtFeedLoc.Text = "Error in Reading Feed. Try Again later!!";
return;
}
using (Stream s = e.Result)
{
SyndicationFeed feed;
List<SyndicationItem> feedItems = new List<SyndicationItem>();
using (XmlReader reader = XmlReader.Create(s))
{
feed = SyndicationFeed.Load(reader);
foreach (SyndicationItem feedItem in feed.Items)
{
feedItems.Add(feedItem);
}
var posts = from item in feedItems
select new RSSFeed()
{
Title = item.Title.Text,
NavURL = item.Links[0].Uri.AbsoluteUri,
Description = item.Summary.Text
};
dGrid.ItemsSource = posts;
dGrid.Visibility = Visibility.Visible;
}
}
}
private void btnFetch_Click(object sender, RoutedEventArgs e)
{
if(txtFeedLoc.Text != String.Empty)
LoadRSS(txtFeedLoc.Text.Trim());
}
}
public class RSSFeed
{
public string Title { get; set; }
public string NavURL { get; set; }
public string Description { get; set; }
}
}
VB.NET
Imports System
Imports System.Xml
Imports System.Net
Imports System.Windows
Imports System.Windows.Controls
Imports System.ServiceModel.Syndication
Imports System.IO
Imports System.Collections.Generic
Imports System.Linq
Namespace ConsumingRSS
Partial Public Class Page
Inherits UserControl
Public Sub New()
InitializeComponent()
End Sub
Protected Sub LoadRSS(ByVal uri As String)
Dim wc As New WebClient()
AddHandler wc.OpenReadCompleted, AddressOf wc_OpenReadCompleted
Dim feedUri As New Uri(uri, UriKind.Absolute)
wc.OpenReadAsync(feedUri)
End Sub
Private Sub wc_OpenReadCompleted(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs)
If e.Error IsNot Nothing Then
txtFeedLoc.Text = "Error in Reading Feed. Try Again later!!"
Return
End If
Using s As Stream = e.Result
Dim feed As SyndicationFeed
Dim feedItems As List(Of SyndicationItem) = New List(Of SyndicationItem)()
Using reader As XmlReader = XmlReader.Create(s)
feed = SyndicationFeed.Load(reader)
For Each feedItem As SyndicationItem In feed.Items
feedItems.Add(feedItem)
Next feedItem
Dim posts = _
From item In feedItems _
Select New RSSFeed()
item.Links(0).Uri.AbsoluteUri, Description = item.Summary.Text
item.Title.Text, NavURL = item.Links(0).Uri.AbsoluteUri, Description
Title = item.Title.Text, NavURL
dGrid.ItemsSource = posts
dGrid.Visibility = Visibility.Visible
End Using
End Using
End Sub
Private Sub btnFetch_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
If txtFeedLoc.Text <> String.Empty Then
LoadRSS(txtFeedLoc.Text.Trim())
End If
End Sub
End Class
Public Class RSSFeed
Private privateTitle As String
Public Property Title() As String
Get
Return privateTitle
End Get
Set(ByVal value As String)
privateTitle = value
End Set
End Property
Private privateNavURL As String
Public Property NavURL() As String
Get
Return privateNavURL
End Get
Set(ByVal value As String)
privateNavURL = value
End Set
End Property
Private privateDescription As String
Public Property Description() As String
Get
Return privateDescription
End Get
Set(ByVal value As String)
privateDescription = value
End Set
End Property
End Class
In the code displayed above, I have created a LoadRSS() method which creates the WebClient object, adds the handler and initiates the request. To request a resource as a stream, you must call an OpenReadAsync method overload.
In the wc_OpenReadCompleted callback function, we check the Error property for errors. If there aren’t any, we use the XMLReader class to read the RSS xml and then load the Syndication Feed from the reader. I have also created a ‘RSSFeed’ class to store some properties of the RSS. I then use LINQ to loop through the items collection of SyndicationFeed and set the properties of the ‘RSSFeed’ class. The DataGrid is then bound to the collection.
In order to display the RSS data, I have set up each column individually in the DataGrid to format the content as required. I got some ideas of creating templates and databinding from over here.
The mark up is shown below:
<data:DataGrid x:Name="dGrid" AutoGenerateColumns="False" IsReadOnly="True" CanUserSortColumns="True" Grid.Row="1" Grid.ColumnSpan="2">
<data:DataGrid.Columns>
<data:DataGridTemplateColumn>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<HyperlinkButton Content="{Binding Title}" NavigateUri="{Binding NavURL}" TargetName="_blank"/>
<local:HtmlTextBlock Text="{Binding Description}" TextWrapping="Wrap" UseLayoutRounding="True"/>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
</data:DataGrid>
If you noticed, I am using the HtmlTextBlock control instead of the TextBlock control since my RSS contains ‘FeedFlare’ (with HTML tags). The HtmlTextBlock control supports displaying and rendering HTML in the TextBlock and can be downloaded from here.
The result is as displayed below:
Cross-Domain Calls
In order to Make a Service Available Across Domain Boundaries, we require either a crossdomain.xml (supported by Flash) or the clientaccesspolicy.xml(supported by Silverlight). If these xml files are not kept in the root of the root domain, we get a 404 error.
However the best part in accessing a RSS feed hosted on FeedBurner is that the crossdomain.xml file is available on the FeedBurner domain and hence allows us to access RSS feeds. Cool!
The SyndicationFeed class makes it almost effortless to read RSS. It’s good to have a class that does the parsing automatically for us (earlier I used the XMLReader). I hope this article was useful and I thank you for viewing it. The entire source code of this article can be downloaded from here.
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of
DotNetCurry,
DNC Magazine for Developers,
SQLServerCurry and
DevCurry. He has also authored a couple of books
51 Recipes using jQuery with ASP.NET Controls and
The Absolutely Awesome jQuery CookBook.
Suprotim has received the prestigious Microsoft MVP award for Sixteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.
Get in touch with him on Twitter @suprotimagarwal or at LinkedIn