Extending ASP.NET Web Pages - Create Your Own Helpers

4.77 (13 votes)

The Beta1 version of WebMatrix comes with a number of Web Pages helpers to make your life simple. These include helpers to work with file uploading, images, email, caching, grids, Twitter feeds and a lot more. I suspect that as the product evolves towards full release, more will be added. Nevertheless, the developers of WebMatrix and Web Pages cannot anticipate every requirement, so there will be a time when you feel you need something extra. Adding your own helpers is quite easy, as this article shows.

One area where there is no helper currently is consuming RSS feeds. So I am going to build a helper that allows the user to display part of an RSS feed on a web page. The code that I will show here does the job, although it is far from perfect. Its purpose is to illustrate the main points involved in creating a helper, and not to be a production-ready example of how to produce an Rss reader. It lacks far too much exception handling for one thing, and I felt that adding too much would obscure the main thrust of this article. For that reason, I won't delve too deeply into the actual helper code, but I will draw attention to the key areas that should be considered when developing helpers.

The first thing to do is to add a new folder to the web site. This folder must be named "App_Code". Future versions of WebMatrix may offer this in something like an "Add New ASP.NET Folder" menu option at some stage, but that's not currently available. App_Code is a special folder. ASP.NET will compile anything inside this the first time the site is run, and it will be available to other code throughout the site. Within that folder, add a new file. The type of file is a C# class file.

Call this file "RssReader.cs", and then remove all the code that's in it, then replace it with the following:

using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Xml.Linq;


public static class RssReader
{
  public static HtmlString GetHtml(string url, int numberOfItems = 5, 
  				bool showDescription = true, bool showDateCreated = true, int width = 300)
  {
    TagBuilder divTag = new TagBuilder("div");
    TagBuilder pTag = new TagBuilder("p");
    TagBuilder anchor = new TagBuilder("a");
    
    var doc = XDocument.Load(url);
    XNamespace a10 = doc.Root.GetNamespaceOfPrefix("a10");
    var items = doc.Descendants("item").Select(feed => new
    {
      Title = feed.Element("title"),
      Link = a10 != null ? feed.Element(a10 + "link").Attribute("href").Value : feed.Element("link").Value,
      Date = feed.Element("pubDate"),
      Description = feed.Element("description")
    }).Take(numberOfItems);

    divTag.MergeAttribute("id", "rssDiv");
    divTag.MergeAttribute("style", "width:" + width + "px");
    foreach(var item in items)
    {
      anchor.InnerHtml = item.Title.Value;
      anchor.MergeAttribute("href", item.Link);
      pTag.InnerHtml = anchor.ToString();
      divTag.InnerHtml += pTag.ToString();
      if(showDateCreated)
      {
        TagBuilder pTag2 = new TagBuilder("p");
        DateTime dateValue;
        if(DateTime.TryParse(item.Date.Value, out dateValue))
        {
          pTag2.InnerHtml = dateValue.ToString("G");
        }
        divTag.InnerHtml += pTag2.ToString();
      }
      if(showDescription)
      {
        TagBuilder pTag3 = new TagBuilder("p");
        pTag3.InnerHtml = item.Description.Value;
        divTag.InnerHtml += pTag3.ToString();
      }
    }
    return new HtmlString(divTag.ToString());
  }
}

Right - before you pass out, let me explain how this code works. It's simple, really. The first 5 lines make certain namespaces available to the code so that when we reference items within them, we do not have to use their fully qualified name. You can see public static HtmlString near the beginning of the code. The fully qualified reference for HtmlString is System.Web.Mvc.HtmlString, and without adding a using statement to make System.Web.Mvc available to the class file, we would have to type the fully qualified name every time we wanted to reference HtmlString. System.Web.Mvc also makes TagBuilder available in a short-handed fashion too.

You should also note that both the RssReader class and the GetHtml method are static. This means we do not have to create an instance of RssReader in order to call the GetHtml method from a web page or other code. This is typical of helper methods, and other utility methods in general.

The parameters for the GetHtml method are worth noting too. The first one is a required parameter. In other words, when the GetHtml method is called, you must pass a string as an argument. Ideally, that string will be a valid Uri pointing the the location of an RSS feed. There's no checking in the method to make sure it is. This is one of the missing pieces of exception management I mentioned earlier. The other 4 parameters are all optional. In other words, the caller does not need to provide any values for them. You can see they are optional as they have default values. Optional parameters were added in C# 4.0, and are a terrific way to add flexibility without the need for creating multiple overloaded versions of the GetHtml method. Optional parameters must always be declared AFTER all required parameters (except for one case involving params arrays, but that's not important here).

The next thing of note is the 3 TagBuilder objects that are created. TagBuilder is a very useful class introduced with ASP.NET MVC if you ever want your helper to render html elements, along with HtmlString. TagBuilders allow you to specify an html tag such as a <p> for paragraph, or <input type="text" />, without having to fiddle around with building the string manually, which can be error prone, especially when trying to deal with embedded double quotes. HtmlString ensures that the resulting concoction of tags and html are rendered as html, rather than being html-encoded.

The RSS feed is obtained via the XDocument.Load() method, which is part of System.Xml.Linq. I'm not going to go into too much detail about Linq To XML but it is a nice way to deal with XML, which is what an RSS feed is, once you get to grips with the basics of the subject (which I hope to do one day...). Suffice to say that assuming the Load method actually managed to retrieve the XML file (again, error checking should be in place there) some items are retrieved from it and used to build a collection of objects. How many objects are built depends on the value passed in to the numberOfItems parameter. If no value was provided, it will default to 5 items.

Now that we have a collection of objects, they are iterated through to build some html. The resulting RSS feed will be housed in a div element, with a width of 300px by default. This can be customised by the user if they choose to pass a value into the method. While iterating the collection, the code builds an html link from the item's Link property, and puts that into a paragraph element. Then it checks ot see if the caller decided to include the published date in their RSS Reader. If they did, or left the default value, this is added to another paragraph element. Finally if the showDescription value is true, item descriptions are added to another paragraph, before looping to the next item in the collection. Finally, the whole lot is returned to the caller as a string of Html.

Before showing how this code can be used in a cshtml file, I will take a second to review the key points about constructing helpers:

  1. App_Code is the place for class files which need to be made available across the site
  2. Helper classes and methods should invariably be static
  3. HtmlStrings ensure that anything to be rendered by the browser is sent as Html
  4. TagBuilders make building valid Html a lot easier
  5. Optional parameters save a lot of code by reducing the need to create overloaded methods

Here are a couple of ways in which the helper can be called within a Web Pages file:


@RssReader.GetHtml("http://www.mikesdotnetting.com/rss")

@RssReader.GetHtml("http://www.mikesdotnetting.com/rss/", showDescription: false, width: 400, numberOfItems: 10)

The first line makes use of all default values applied to the optional parameters, while the second one changes the number of items to 10, increases the width of the housing div, and elects not to display descriptions. It leaves the helper to show the date. You should also notice that the optional parameters arguments are prefixed by the name of the parameter, and are not added to the method call in the order in which they appear in the method parameter list itself. Named parameters was also introduced in C# 4.0, and the link to Optional parameters provided earlier explains more about this too.

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 26087

9 Comments

- Alvanti

Hey! You explain this so simply even I can do this! Fantastic!

- Alex

Nice article!

- dotnetcoder

Awesome introduction to this topic. This stuff normally goes over my head. Mike, why do we have to use the "out" in this?:

if(DateTime.TryParse(item.Date.Value, out dateValue))

- Mike

@dotnetcoder

DateTime.TryParse() takes an output parameter. The only reason for declaring dateValue is as a place to store the parsed string as a DateTime, so it's passed in by reference.

http://msdn.microsoft.com/en-us/library/aa645764(VS.71).aspx

- erik

how come you dont need: using Microsoft.WebPages.Helpers; ?

I've seen that in other tutorials

- Mike

@erik

I suspect that the other examples you have seen (possibly the Gravatar one?) have actually referenced a Web Pages helper within the method body, which is why the using would be needed.

My example doesn't. It's makes use of classes from System.Linq, System.Web.Mvc and System.Xml.Linq, but not Microsoft.WebPages.Helpers.

- marc

hi , nice but i cant make it work , do i need to install asp mvc or something ?

- Mike

@marc

MVC should be installed as part of the install of WebMatrix. You would be better off posting a question to the WebMatrix forum and explain what error you get when you try to get it to work.

- Nandha kumari

Nice.

Recent Comments

Pam 30/08/2017 11:30
In response to Sending Email in Razor Pages
Mike, RazorPages sound like a nice choice for somebody still working in ASP classic who wants to to...

Robby Robson 15/08/2017 00:43
In response to Routing in Razor Pages
Mike: great stuff. Now that .Core Standard 2.0 is formally out, how soon will you rewrite your book...

Satyabrata Mohapatra 28/07/2017 08:59
In response to Sending Email in Razor Pages
Bit off topic, but congratulation sir for your MVP award. You deserve it !!!...

Satyabrata Mohapatra 23/07/2017 16:43
In response to Razor Pages - The Elevator Pitch
@Dale Severin You can continue to build apps using asp.net web pages....

Satyabrata Mohapatra 23/07/2017 16:40
In response to Sending Email in Razor Pages
Thanks for sharing...learned a lot...

Gfw 22/07/2017 11:53
In response to Sending Email in Razor Pages
Question... Does System.Net.Mail support SSL?...

Dale Severin 20/07/2017 03:38
In response to Razor Pages - The Elevator Pitch
I work with razor web pages extensively. I appreciate the rapid development it permits me to I am as...

Obinna Okafor 14/07/2017 01:19
In response to Routing in Razor Pages
Thank you, Mike. Good post....

Satyabrata Mohapatra 11/07/2017 16:02
In response to Routing in Razor Pages
Very powerful routing system!!...

Cyrus 05/07/2017 03:41
In response to Razor Pages - Getting Started With The Preview
How can I trim packages and services as much as possible to use just razor pages? I don’t want to to...