Skip to content
Nubgrammer
Menu
  • Home
  • Contact Me
Menu

Write an RSS Feed Aggregator, Displaying the Feed Content

Posted on November 15, 2017September 24, 2018 by Tyler Sells

TL;DR:

We’re almost done with this series.  Hallelujah?  This one is about building the class that will display all of our feed contents and an HTML helper class that will help us build the HTML that goes into the WebBrowser control.  Code is here, video is here.

Displaying the Feed Content

So again, a little copy paste action: Code is here, video is here (or you could scroll down a little more too)

There are only two classes we’re gonna add.  DisplayManager.cs and HtmlWriter.cs

DisplayManager.cs

So this is most of the class.  I’m going to leave out the LoadFeedInBrowser function for now, but I’ll come back to it after I explain the HtmlWriter class.

 class DisplayManager
 {
     private FeedsManager Feeds { get; }
     private WebBrowser Browser { get; }
     private MetroLabel TimeStamp { get; }
     public MetroTile SelectedTile { get; set; }
     public DisplayManager(FeedsManager feeds, WebBrowser browser, MetroLabel timeStamp)
     {
         Feeds = feeds;
         Browser = browser;
         TimeStamp = timeStamp;
     }
     public void AddTile(MetroPanel panel, MetroTile tile)
     {
         var numTiles = panel.Controls.OfType().Count();
         tile.Location = new Point(numTiles * 155, 0);
         panel.Controls.Add(tile);
     }
     public MetroTile CreateTile(Feed feed)
     {
         MetroTile tile = new MetroTile();
         tile.Text = feed.Name;
         tile.Style = MetroColorStyle.Purple;
         tile.TileTextFontSize = MetroTileTextSize.Tall;
         tile.UseStyleColors = true;
         tile.Theme = MetroThemeStyle.Dark;
         tile.Tag = feed;
         tile.Click += new EventHandler(LoadFeedInBrowser);
         tile.Size = new Size(150, 100);
         return tile;
     }
     public void UpdateTiles(MetroPanel panel)
     {
         UnloadTiles(panel);
         LoadTiles(panel);
     }
     public void LoadTiles(MetroPanel panel)
     {
         foreach (Feed feed in Feeds.GetFeedsFromConfig())
         {
             var tile = CreateTile(feed);
             AddTile(panel, tile);
         }
     }
     public void UnloadTiles(MetroPanel panel)
     {
         SelectedTile = null;
         var tiles = panel.Controls.OfType().ToArray();
         for (var i = tiles.Count() - 1; i >= 0; i--)
         {
             panel.Controls.Remove(tiles[i]);
             tiles[i].Dispose();
         }
         panel.AutoScrollPosition = new Point(0, 0);
     }
 }
Translation:
  • DisplayManager() takes a few paramaters.  We need a list of the feeds we’re going to display, we’re going to need a reference to the WebBrowser control that we’re going to load them into, and finally, we’ll need a reference to the MetroLabel control that we’re going to update with a timestamp
  • AddTile() takes a MetroPanel and a MetroTile, figures out where to place the tile on the panel (new Point(numTiles * 155, 0)), and finally adds it to the panel.
  • CreateTile() needs a Feed.  This function handles all of the busy work associated with creating a new MetroTile and sets the Tag as the given Feed.  Several form controls in ASP.NET let us use the Tag property like this.  It’s basically a handy little holder variable that we can point to whatever we want.  We also register a click event for each tile that’s created.  More on that later too.
  • UpdateTiles() lets us pass in any MetroPanel we want and then effectively refresh it by unloading everything and loading it back.  This is handy for making changes to the feeds from the configuration side of things.
  • LoadTiles() loops through all of the feeds in our list, creates a tile from each one, and then adds it.  (Remember what we passed in the constructor?)
  • UnloadTiles() does the exact opposite of LoadTiles().  Go figure.  However, we also do a couple of other things here too.  We have to set the SelectedTile property to null AND reset the scroll position of the panel.  If we deleted several tiles at once, we might run into a situation where the panel is scrolled out of range and from personal experience, that bug is not pretty.  You can see what happens here.

HtmlWriter.cs

class HtmlWriter
{
     private string html;
     private string css;
     public string HTML
     {
          get
          {
               return "<style>\n" + css + "\n</style>\n" + html;
          }
     }
     public HtmlWriter()
     {
          css = "";
          html = "";
     }
     public string CreatePairedTag(string tag, string classes, string contents)
     {
          var html = "<" + tag + " class=\"" + classes + "\">\n" + contents + "\n</" + tag + ">\n";
          return html;
     }
     public string CreateLink(string href, string classes, string contents)
     {
          var link = "<a class=\"" + classes + "\" href=\"" + href + "\">" + contents + "</a>\n";
          return link;
     }
     public string CreateSingleTag(string tag, string classes)
     {
          var html = "<" + tag + " class=\"" + classes + "\">\n";
          return html;
     }
     public void AddTag(string tag)
     {
          this.html += tag;
     }
     public void AddCSSRule(string selector, string[] properties)
     {
          var css = selector + "{\n";
          foreach (string property in properties)
          {
               css += property + ";\n";
          }
          css += "}\n";
          this.css += css;
     }
}

All this class does is help us write our HTML tags.  Instead of piecing together strings and then inserting c# variables and objects in funky places, we can pass them neatly to these methods and have it format everything for us.  You could expand on this if you wanted to, but it serves it purpose pretty well in its current form.

Why do we need HTML anyway?

If you remember way back in the beginning of this series, we’re using a WebBrowser control to display the all of the feed content.  RSS Feeds already include HTML in their XML tags, so using that control just makes sense.  However, we have to include some other formatting to make all of the RSS items look cohesive on a single page.  That’s why we need to throw a little of our own HTML in the mix and this class is a clean way to do it.

  • Our two variables, html and css just store our markup as we create it.
  • The property HTML combines the css and the html strings together and sends us the full markup string.
  • The three Create functions all accomplish similar goals.  They build html syntax around the variables we pass into them.  We can even give them css classes too.
  • The AddTag() function just adds the passed tag to the html string.
  • The AddCSSRule() function is a little different.  The concept is the same in that it builds the proper css syntax around the items we’re passing in, but it has to loop through all of the (properly formatted) properties you give it in order to add them.  It’s also worth noting that you wouldn’t want have more than one set of rules for each selector, unless you really know what you’re doing with your css.

It’s also worth noting that this does not provide completely valid html.  There is no doctype declaration nor are the html, head, or body tags.  You could fix that if you wanted to, but since we are just displaying this stuff locally, in a WinForm app, I think the html gods can forgive us for this one.

Putting them together

In our DisplayManager class, we also have this method:

private void LoadFeedInBrowser(object sender, EventArgs e)
{
    var html = new HtmlWriter();
    var feed = (Feed)((MetroTile)sender).Tag;
    try
    {
        //feed.LoadFeed() <- this works but it is more correct to use the UpdateFeed() method
        feed.UpdateFeed();
        var feedNameTag = html.CreatePairedTag("p", "", feed.Name);
        html.AddTag(feedNameTag);

        string[] htmlCss = { "font: 15px Arial" };
        html.AddCSSRule("html", htmlCss);

        string[] wrapperCss = { "display: block", "height:400px", "overflow: auto", "border:1px solid #444", "border-radius:50px", "margin: 5px" };
        html.AddCSSRule(".wrapper", wrapperCss);

        string[] sectionCss = { "background: #333", "color: #EEE" };
        html.AddCSSRule(".section", sectionCss);

        string[] hrCss = { "height: 10px", "background: #999" };
        html.AddCSSRule("hr", hrCss);

        foreach (Item item in feed.Items)
        {
            var pubDateTag = html.CreatePairedTag("div", "", item.PubDate);
            var hrTag = html.CreateSingleTag("hr", "");
            var descriptionTag = html.CreatePairedTag("p", "", item.Description);
            var linkTag = html.CreateLink(item.Link, "", "View on Site");
            var wrapperTag = html.CreatePairedTag("div", "wrapper", item.Title + descriptionTag);
            var fullTag = html.CreatePairedTag("div", "section", pubDateTag + linkTag + wrapperTag + hrTag);
            html.AddTag(fullTag);
        }
        Browser.DocumentText = html.HTML;
        var time = DateTime.Now;
        TimeStamp.Text = "Feed last pulled at " + time.ToString("t") + " on " + time.ToString("D");
        SelectedTile = (MetroTile)sender;
    }
    catch (Exception ex)
    {
        AggForm.Alert(ex.Message);
    }
}

This function gets called every time a tile is clicked.  We create an instance of our HtmlWriter class and we also do some casting to get the appropriate feed from the MetroTile that was clicked (var feed = (Feed)((MetroTile)sender).Tag;).  Then it gets interesting:

  • At this point we want the feed to actually load all of the XML data from the RSS link stored in it, so we call it’s UpdateFeed method (you could also use LoadFeed to get the feed content).  We have to do a try catch block here because our feed could throw an exception if the URL it contains is invalid.
  • Once we have the feed content loaded correctly into the feed object, we can start creating HTML tags for all of the properties.
  • That’s what the rest of this function does.  It’s pretty straight forward except for the foreach loop.
  • In HTML, we can nest elements (tags).  In order to nest elements with our HtmlWriter class, you have to concatenate the desired tags together and pass them into the Create function as one variable.
  • Finally, we set the DocumentText of the WebBrowser control equal to html.HTML, which contains all of the css and html that we just created, get the current time and update the TimeStamp MetroLabel, and set the SelectedTile property equal the whichever MetroTile sent this click event.  (We need this so we can have our Update News button call the click event whenever we want to refresh the feed content)

That’s about it

That’s about all for this one.  The video will show how to hook the DisplayManager up to the rest of the form.  Once it’s created in the form, you just have to update the tiles anytime you make a change in the configuration (FeedsManager) and hookup the Update News button.  It’s also worth noting that because this isn’t a Web based RSS reader and some RSS feeds contain relative links that don’t include the domain name in them, most of those links won’t work so we have to provide an alternative link that will.  That’s what var linkTag = html.CreateLink(item.Link, "", "View on Site"); is for.

Author: Tyler Sells

Github

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • More
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to email a link to a friend (Opens in new window) Email
  • Click to share on Reddit (Opens in new window) Reddit

Like this:

Like Loading...

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Follow me on Twitter

My Tweets

Github Repos

vtsells (Tyler Sells)

Tyler Sells

vtsells
http://www.nubgrammer.com
Joined on Jun 21, 2017
9 Public Repositories
100DaysOfCode
embers
MultiSelect
MVC-Project-Start
nubgrammer.com
PermIT
Spray
vtsells.github.io
Wizard
0 Public Gists

Categories

  • #100DaysOfCode (4)
  • ASP.NET (7)
  • ASP.NET Core (1)
  • ASP.NET MVC (3)
  • CSS (4)
  • General (13)
  • JS (3)
  • LESS (2)
  • Snippets (4)
  • Tools (4)
  • Tutorials (9)

Recent Posts

  • Creating a Knockout.js project on Codepen
  • 100DaysOfCode Day 3 – A State of Mind
  • 100DaysOfCode Day 2 – The Building Blocks
  • 100DaysOfCode Day 1 (Sort of cheated already)
  • Committing to #100DaysOfCode
© 2025 Nubgrammer | Powered by Superbs Personal Blog theme
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
Do not sell my personal information.
Cookie SettingsAccept
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
CookieDurationDescription
cookielawinfo-checkbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytics
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
Others
Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
SAVE & ACCEPT
%d

    Privacy Policy