A Templated ASP.NET RSS Feed Reader Control

Update 20070330:

By popular demand (well a couple people) I have included a sample project with everything needed to get started using this control.

Basically just a sample web project, click here to get the zipped archive. You might notice things are done a little different, I include the System.ServiceModel.Syndication namespace on my page so that I can case the Container.DataItem to a SyndicationItem and access its properties that way, stops using Eval, but each to their own.


Hope that makes it easier for some, will be including full source in future too :).

Project Files:
TemplatedRSSFeedReader.zip

Thanks
Stefan


Update 20070319:

Today I attended the Heros Happen event in Perth, and I learnt something very very cool, you see how I wrote my own classes and code to load the syndicated feed in, then used LINQ to XML to load that into my classes. During Dave Glovers presentation on WCF he had a sample in there that was building an RSS feed, and I could see he was using some classes to do so. What I then noticed is these classes are new in .NET 3.5 and will make creation of this control 100 times easier plug give us full support of the RSS 2.0 spec and allow us to access all feed proprties :) :) and will mean *removing* code and making things much simpler.

Firstly we need to add a reference to the System.ServiceModel.Web assembley, this is where the magic is. Then add a using System.ServiceModel.Syndication; to the RSSReader.cs class, we then have access to these helper classes which allow us to easily read and create RSS 2.0 and ATOM 1.0 feeds, as I am only supporting RSS I will only use the RSS formatter. To read our feed we need to do the below:

Firslty read the feed into an XMLReader:

XmlReader reader = XmlReader.Create("http://weblogs.asp.net/stefansedich/rss.aspx");

Then create an instance of the RSS20FeedFormatter and then make this read the data from the xmlreader:

Rss20FeedFormatter feedFormatter = new Rss20FeedFormatter();
feedFormatter.ReadFrom(reader);


We now can get access to the feed by using feedFormatter.Feed, this gives us an instance of a SyndicationFeed class which gives us access to all items and properties of our feed. The only difference now is that as some properties are a little different accessing them is different.So instead of feed.Title you use feed.Title.Text instead. The good thing with this is, 1. We have removed the need to do this manually and 2. We have all properties that belong to an RSS feed.

The updated RSSReader class is below with the code changes. You can now remove the Feed and FeedItem classes we created earlier as they are needed no more. It just goes to show we learn something new every day.....


Code:

    /// <summary>

    /// An RSS Feed reader server control, it will basically aggregate rss feeds and display the content

    /// </summary>

    public class RSSReader : CompositeControl, INamingContainer {

 

        #region Properties

 

        /// <summary>

        /// The URL of the feed to display

        /// </summary>

        public string FeedURL {

            get {

                return ViewState["FeedURL"] != null ? ViewState["FeedURL"].ToString() : string.Empty;

            }

            set {

                ViewState["FeedURL"] = value;

            }

        }

 

        /// <summary>

        /// The number of items to show from the feed, 10 is the default.

        /// </summary>

        public int FeedItemCount {

            get {

                return ViewState["FeedItemCount"] != null ? (int)ViewState["FeedItemCount"] : 10;

            }

            set {

                ViewState["FeedItemCount"] = value;

            }

        }

 

        /// <summary>

        /// The header template for this control

        /// </summary>

        [TemplateContainer(typeof(RSSReaderDataItem)), PersistenceMode(PersistenceMode.InnerProperty)]

        public ITemplate HeaderTemplate { get; set; }

 

        /// <summary>

        /// The footer template for this control

        /// </summary>

        [TemplateContainer(typeof(RSSReaderDataItem)), PersistenceMode(PersistenceMode.InnerProperty)]

        public ITemplate FooterTemplate { get; set; }

 

        /// <summary>

        /// The item template for this control

        /// </summary>

        [TemplateContainer(typeof(RSSReaderDataItem)), PersistenceMode(PersistenceMode.InnerProperty)]

        public ITemplate ItemTemplate { get; set; }

       

        #endregion

 

        #region Constructor

 

        // Default constructor

        public RSSReader() {

           

        }

 

        #endregion       

 

        #region Child Control Creation

 

        /// <summary>

        /// Creates child controls for this control, first create the header

        /// based on template, then the items, then finally the footer.

        /// </summary>

        protected override void CreateChildControls() {

 

            // Get our RSS feed data, this is returned as a collection

            // of the RSSReaderDataItem controls.

            SyndicationFeed feed = this.GetRSSData();           

 

            if (feed != null) {

 

                // Create the header template, and add to the controls.

                if (this.HeaderTemplate != null) {

 

                    // Create the dataitem control for the header

                    // and assign it the current feed as its dataitem.

                    RSSReaderDataItem header = new RSSReaderDataItem() {

                        DataItem = feed

                    };

 

                    // Instantiate template, add to controls and

                    // databind header.

                    this.HeaderTemplate.InstantiateIn(header);

                    this.Controls.Add(header);

                    header.DataBind();

 

                }

 

                if (this.ItemTemplate != null) {

                    foreach (SyndicationItem dataItem in feed.Items) {

 

                        // Create an item child control and

                        // assign the current feed item as its dataitem.

                        RSSReaderDataItem item = new RSSReaderDataItem() {

                            DataItem = dataItem

                        };

 

                        // Instantiate the item template, add to controls

                        // and databind the item.

                        this.ItemTemplate.InstantiateIn(item);

                        this.Controls.Add(item);

                        item.DataBind();

 

                    }

                }

 

                if (this.FooterTemplate != null) {

 

                    // Create the footer dataitem

                    // and assign it the current feed as its dataitem.

                    RSSReaderDataItem footer = new RSSReaderDataItem() {

                        DataItem = feed

                    };

 

                    // Instantiate the template, add to controls

                    // and databind the footer.

                    this.FooterTemplate.InstantiateIn(footer);

                    this.Controls.Add(footer);

                    footer.DataBind();

 

                }

 

            }       

 

        }

 

        #endregion

 

        #region RSS Data Retrieval

 

        /// <summary>

        /// Fetched the RSS data from the current RSS Feed

        /// </summary>

        /// <returns>A collection of RSSReaderDataItems</returns>

        private SyndicationFeed GetRSSData() {

            SyndicationFeed feed = null;

 

            // Only do if we have a feed url specified.

            if (!string.IsNullOrEmpty(this.FeedURL)) {

 

                // Get the current feed and load into an XMLReader

                using (XmlReader reader = XmlReader.Create(this.FeedURL)) {                   

                    Rss20FeedFormatter feedFormatter = new Rss20FeedFormatter();

                   

                    // Read the contents of the XMLReader into the FeedFormatter

                    feedFormatter.ReadFrom(reader);

                   

                    // Get the current feed.

                    feed = feedFormatter.Feed;

                }          

               

            }

 

            return feed;

        }

 

        #endregion       

 

    }


And you would change the controls template on the page a little to be able to read the properties:

<cc2:RSSReader ID="RSSReader2" runat="server" FeedURL="http://weblogs.asp.net/stefansedich/rss.aspx">
<HeaderTemplate>
            <div>
                  <b><%# Eval("Title.Text") %></b>                  
                  <br /><br />
       </HeaderTemplate>
       <ItemTemplate>
            <%# Eval("Title.Text") %>
            <br /><br />
            <%# Eval("Summary.Text") %>
            <hr />
        </ItemTemplate>
        <FooterTemplate>
             </div>
         </FooterTemplate>
</cc2:RSSReader>  

 

Thanks
Stefan

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

Hello All,

Bored again tonight thought I would have a play and write a Templated RSS feed reader control, using .NET 3.5. I created a control that you can control with templates, you just set the FeedURL, the FeedItemCount, Header and Footer templates if needed and the ItemTemplate and you are set. This post could get quite lengthy and will have lots of code examples. I kind of assume you know about templated controls in asp.net and do not go much into the details of their workings.


Loading an RSS Feed:

Thought I would make this a little better than just loading the feed in the control. So I ended up creating a Feed class and a FeedItem class, as the names suggest one is the feed and the other represents a feed item. In the Feed class there is a LoadFeed method, this takes the url and limit, then uses LINQ to XML to load the feed. If a limit > 0 is provided it will also limit the number of items in the feed. The code for the classes are below:

/// <summary>
    /// Represents an RSS Feed
    /// </summary>
    public class Feed {

        #region Properties

        /// <summary>
        /// The title of the feed
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// A link to the site that hosts the feed
        /// </summary>
        public string Link { get; set; }
       
        /// <summary>
        /// A description of the feed
        /// </summary>
        public string Description { get; set; }       

        /// <summary>
        /// The items that are contained within this feed
        /// </summary>
        public IEnumerable<FeedItem> Items { get; set; }

        #endregion

        #region Feed Loading

        /// <summary>
        /// Given a feed URL, loads that feed.
        /// </summary>
        /// <param name="feedURL"></param>
        public void LoadFeed(string feedURL, int limit) {
           
            // Load the feed from the URL we have set
            // as a property on this control.
            XDocument rssFeed = XDocument.Load(feedURL);

            // Get the channel element.
            var channel = rssFeed.Descendants("channel").First();

            // Set the main feed values.
            this.Title = channel.Element("title").Value;
            this.Description = channel.Element("description").Value;
            this.Link = channel.Element("link").Value;

            // Process all the feed items and add them to the
            // main feed class.
            this.Items = (from item in rssFeed.Descendants("item")
                          select new FeedItem {
                              Title = item.Element("title").Value,
                              Description = item.Element("description").Value,
                              Link = item.Element("link").Value
                          });

            if (limit > 0) {
                // If the limit is > 0 then limit the
                // number of items in our feed.
                this.Items = this.Items.Take(limit);
            }

        }

        #endregion

    }

The feed class just has the feeds main properties and a collection of items, the LINQ to XML takes care of loading the items and relevant properties.

/// <summary>
    /// Represents an RSS item
    /// </summary>
    public class FeedItem {

        /// <summary>
        /// The title of the item
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// The items description
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// A link to the item
        /// </summary>
        public string Link { get; set; }       

    }

The feeditem class just holds the properties for an item in our feed. One thing to note here is I did not implement all the properties according to RSS spec. I may do this at a future time thought and will update this post then.


The RSS Reader Control:

The control is where all the magic happens, basically to create a new templated control you need to create a control and add the template properties, in my case HeaderTemplate, FooterTemplate and ItemTemplate. All these template properties are an RSSReaderDataItem, this class internally just has a DataItem property which will either be the feed or the actual feed item incase of the ItemTemplate.

Basically my RSSReader control inherits from the CompositieControl class and then I override the CreateChildControls method, in here I get the RSS feed data and then begin to create the controls. First I create an instance of the header control and load the current HeaderTemplate into it, I set the dataitem to be the feed and then bind it. The same thing is done for the footer but using the FooterTemplate.

For each item in our feed I do the same thing just create an item, load the ItemTemplate, then set the DataItem to the current FeedItem, and databind. The controls code is listed below:

/// <summary>
    /// An RSS Feed reader server control, it will basically aggregate rss feeds and display the content
    /// </summary>
    public class RSSReader : CompositeControl, INamingContainer {

        #region Properties

        /// <summary>
        /// The URL of the feed to display
        /// </summary>
        public string FeedURL {
            get {
                return ViewState["FeedURL"] != null ? ViewState["FeedURL"].ToString() : string.Empty;
            }
            set {
                ViewState["FeedURL"] = value;
            }
        }

        /// <summary>
        /// The number of items to show from the feed, 10 is the default.
        /// </summary>
        public int FeedItemCount {
            get {
                return ViewState["FeedItemCount"] != null ? (int)ViewState["FeedItemCount"] : 10;
            }
            set {
                ViewState["FeedItemCount"] = value;
            }
        }

        /// <summary>
        /// The header template for this control
        /// </summary>
        [TemplateContainer(typeof(RSSReaderDataItem)), PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate HeaderTemplate { get; set; }

        /// <summary>
        /// The footer template for this control
        /// </summary>
        [TemplateContainer(typeof(RSSReaderDataItem)), PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate FooterTemplate { get; set; }

        /// <summary>
        /// The item template for this control
        /// </summary>
        [TemplateContainer(typeof(RSSReaderDataItem)), PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate ItemTemplate { get; set; }
       
        #endregion

        #region Constructor

        // Default constructor
        public RSSReader() {
           
        }

        #endregion       

        #region Child Control Creation

        /// <summary>
        /// Creates child controls for this control, first create the header
        /// based on template, then the items, then finally the footer.
        /// </summary>
        protected override void CreateChildControls() {

            // Get our RSS feed data, this is returned as a collection
            // of the RSSReaderDataItem controls.
            Feed feed = this.GetRSSData();

            if (feed != null) {

                // Create the header template, and add to the controls.
                if (this.HeaderTemplate != null) {

                    // Create the dataitem control for the header
                    // and assign it the current feed as its dataitem.
                    RSSReaderDataItem header = new RSSReaderDataItem() {
                        DataItem = feed
                    };

                    // Instantiate template, add to controls and
                    // databind header.
                    this.HeaderTemplate.InstantiateIn(header);
                    this.Controls.Add(header);
                    header.DataBind();

                }

                if (this.ItemTemplate != null) {
                    foreach (FeedItem dataItem in feed.Items) {

                        // Create an item child control and
                        // assign the current feed item as its dataitem.
                        RSSReaderDataItem item = new RSSReaderDataItem() {
                            DataItem = dataItem
                        };

                        // Instantiate the item template, add to controls
                        // and databind the item.
                        this.ItemTemplate.InstantiateIn(item);
                        this.Controls.Add(item);
                        item.DataBind();

                    }
                }

                if (this.FooterTemplate != null) {

                    // Create the footer dataitem
                    // and assign it the current feed as its dataitem.
                    RSSReaderDataItem footer = new RSSReaderDataItem() {
                        DataItem = feed
                    };

                    // Instantiate the template, add to controls
                    // and databind the footer.
                    this.FooterTemplate.InstantiateIn(footer);
                    this.Controls.Add(footer);
                    footer.DataBind();

                }

            }       

        }

        #endregion

        #region RSS Data Retrieval

        /// <summary>
        /// Fetched the RSS data from the current RSS Feed
        /// </summary>
        /// <returns>A collection of RSSReaderDataItems</returns>
        private Feed GetRSSData() {
            Feed feed = new Feed();

            // Only do if we have a feed url specified.
            if (!string.IsNullOrEmpty(this.FeedURL)) {         
                // Load the feed.
                feed.LoadFeed(this.FeedURL, this.FeedItemCount);               
            }

            return feed;
        }

        #endregion       

    }


Also the code for the RSSFeedDataItem is below, this is the control that is used for our Templates, it just contains the DataItem.

    /// <summary>
    /// This is the item control template.
    /// </summary>
    public class RSSReaderDataItem : Control, INamingContainer {

        #region Properties

        /// <summary>
        /// The RSS data item.
        /// </summary>
        public object DataItem { get; set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Default, initialize properties.
        /// </summary>
        public RSSReaderDataItem() {
            this.DataItem = null;
        }

        #endregion

    }


Using the control:

Just drop the control on your page and set your templates, and in there you will have access to your feed properties, I am using Eval("NAME") to get the values.

For the header and footer you can get access to the feeds main properties plus items, so you could even show the count of items etc. In the ItemTemplate you only have access to the current feed item, so you can display any of the normal feed item properties.

    <cc2:RSSReader ID="RSSReader2" runat="server" FeedURL="http://weblogs.asp.net/stefansedich/rss.aspx">
            <HeaderTemplate>
                <div>
                    <b><%# Eval("Title") %></b>                   
                    <br /><br />
            </HeaderTemplate>
            <ItemTemplate>
                <%# Eval("Title") %>
                <br /><br />
                <%# Eval("Description") %>
                <hr />
            </ItemTemplate>
            <FooterTemplate>
                </div>
            </FooterTemplate>
    </cc2:RSSReader>   


The End:

The control still has some things that could be done, but I will leave that up to the reader. It would be nice to implement the full RSS spec and use all properties, this could probably be done easier using a 3rd party library for the RSS loading. And even add the ability to add more filtering support to filter by date or even category.

The control shows how easy it is to make templated controls that let you modify the output very easily. This not only makes reuse of your controls better but it lets you fully customise the way your control renders. If you need a primer on templated controls maybe start with this link.


If you would like source code for this just email me and I will send you a copy of a sample project for you to use.

Thanks
Stefan

Published Tuesday, March 18, 2008 10:28 PM by stefan.sedich

Comments

# re: A Templated ASP.NET RSS Feed Reader Control

Tuesday, March 18, 2008 10:28 AM by Edmund

Try having the code colored. It will make a HUGE improvement in the readability of your great posts

# re: A Templated ASP.NET RSS Feed Reader Control

Tuesday, March 18, 2008 11:04 AM by stefan.sedich

Edmund,

Thanks for the tip, looking at it now you are spot on would be alot easier to follow if I just kept my source code color coded. I will aim to do this for all future posts :)

Thanks

Stefan

# re: A Templated ASP.NET RSS Feed Reader Control

Tuesday, March 18, 2008 5:48 PM by stefan.sedich

Dan,

Heh funny thing is I started out that way but decided to simplify it for the purposes of this post. I will look into this again :).

Thanks

Stefan

# re: A Templated ASP.NET RSS Feed Reader Control

Thursday, July 24, 2008 7:49 AM by Haden

I am using asp blog but its RSS i think using this this problem may be erase. Thanks

# re: A Templated ASP.NET RSS Feed Reader Control

Thursday, October 23, 2008 1:11 PM by WotlkPowerLeveling.com

How about caching and exception handling ? We can't make sure that the feed source are always be there when we call it.

# re: A Templated ASP.NET RSS Feed Reader Control

Tuesday, October 20, 2009 1:54 PM by brad

I notice you don't deal with character encodings anywhere within your application.

I've been having issues display a Shift_JIS character encoded feed.

My issue is as follows:

I only want to request the page once, and once I read from it the stream moves forward and then the syndication feed wont know what the encoding is because its removed from the first line.

This is the closest I've got, but .Encoding is still sometimes null (who knows why, loading the exact same feed from two different servers one loads one doesnt).

XmlTextReader textReader = new XmlTextReader(rssUrl);

XmlReader xmlReader = null;

SyndicationFeed feed = null;

try

{

   xmlReader = XmlReader.Create(textReader, new XmlReaderSettings());

   feed = SyndicationFeed.Load(xmlReader);

   feedEncoding = textReader.Encoding.WebName;

   context.Response.Charset = feedEncoding;

}

# re: A Templated ASP.NET RSS Feed Reader Control

Wednesday, July 20, 2011 10:28 AM by kirank28

hi good article,

hei but if i want to retrieve the image from the feed how i do it ?

one more thing how i can link on article title

plz help thx

# re: A Templated ASP.NET RSS Feed Reader Control

Wednesday, July 20, 2011 10:50 AM by kirank28

hi good article,

hei but if i want to retrieve the image from the feed how i do it ?

one more thing how i can link on article title

plz help thx

# re: A Templated ASP.NET RSS Feed Reader Control

Wednesday, September 7, 2011 1:43 PM by kirank28

hi how i can get the url from the feed its urgent

thax