The MVC platform: creating a new HTML helper

Yesterday I’ve presented the main properties and methods of the TagBuilder class. Today, we’re going to reuse those ideas and build an extension method that renders an image inside an anchor (unfortunately, the image helper methods are defined in the futures assembly and the current release of the anchors won’t let you pass HTML for its content). Before going on, I’d like to say two things:

  1. This isn’t going to be a complete example. What I mean by this is that I’m going to pick up a general form of helper and implement only that form (so, you won’t have all those overloads you generally end up having with the official methods);
  2. If I had to implement this scenario in the real world, I’d probably go with “straight HTML” and I’d use the Url helper methods for getting the correct urls.

Having said this, I still believe that building a simple example like this is good and might help you get a better grasp of the code used by the helpers. The idea is to have something like this:

<%=
   Html.ActionLinkWithImage(
           "~/Content/add.png", //relative image url
           "About", //controller action
           "Home", //controller
           null , //route values
           null, //link html attributes
           new { style = "border: none"} //image html attributes
           )
%>

As I said,I’m picking only one form (based on the anonymous object syntax) and I’m using it through out this example. You should be able to extend this sample with more overloads that give you more options and reduce the code you need to write if you’re note interested in setting all these parameters. Let’s see how easy it is to build this helper. Here’s the code:

public static class ImageWithAnchorHelper {
    public static string ActionLinkWithImage( this HtmlHelper htmlHelper,
                                     String relativeImageUrl,
                                     String actionName,
                                     String controllerName,
                                     Object routeValues,
                                     Object linkHtmlAttributes,
                                     Object imageHtmlAttributes )  {
        var url = GetUrlForLink( htmlHelper,
                                 actionName,
                                 controllerName,
                                 routeValues,
                                 null,
                                 null,
                                 null );

        var tagBuilder = BuildTagForLink(
                                    htmlHelper,
                                    GetImageHtml( htmlHelper, relativeImageUrl, imageHtmlAttributes ),
                                    linkHtmlAttributes, url );
        return tagBuilder.ToString( TagRenderMode.Normal );
    }
    private static TagBuilder BuildTagForLink( HtmlHelper htmlHelper,
                                               String imageHtml,
                                               Object linkHtmlAttributes,
                                               String url ) {
        var tagBuilder = new TagBuilder( "a" ) {
                                     InnerHtml = imageHtml
                         };
        tagBuilder.MergeAttributes( new RouteValueDictionary( linkHtmlAttributes ) );
        tagBuilder.MergeAttribute( "href", url );
        return tagBuilder;
    }

    private static String GetIma

geHtml( HtmlHelper helper,
                                        String relativeImageUrl,
                                        Object imageHtmlAttributes )  {
        var imageUrl = new UrlHelper( helper.ViewContext.RequestContext ).Content( relativeImageUrl );
        var imageTag = new TagBuilder( "img" );
        imageTag.MergeAttribute( "src", imageUrl, true );
        imageTag.MergeAttributes( new RouteValueDictionary( imageHtmlAttributes ), true );
        return imageTag.ToString( TagRenderMode.SelfClosing );
    }

    private static String GetUrlForLink( HtmlHelper html,
                                         String actionName,
                                         String controllerName,
                                         Object routeValues,
                                         String protocol,
                                         String hostName,
                                         String fragment ) {
        var urlHelper = new UrlHelper( html.ViewContext.RequestContext );
        var url = urlHelper.Action( actionName,
                                    controllerName,
                                    new RouteValueDictionary(routeValues),
                                    protocol,
                                    hostName );
        if( !String.IsNullOrEmpty( fragment ) ) {
            url += String.Concat (“#”, fragment);
        }
        return url;
    }
}

As you can see, the ActionLinkWithImage method extends the HtmlHelper class and is the only public entry point of these helpers (all the other methods are private). The method starts by getting the url for the current action. If you look at the code used by the helpers, you’ll see that they use the GenerateUrl method for getting the current url. Unfortunately, the method is internal and so we can’t use it without resorting to reflection. On the other hand, we can use the UrlHelper class and its Action method. Lets take a closer look at the GetUrlForLink method. As you can see, we start by instantiating a UrlHelper object:

  var url = urlHelper.Action( actionName,
                                    controllerName,
                                    new RouteValueDictionary(routeValues),
                                    protocol,
                                    hostName );

In this case, protocol and hostname are always null, but I’ve decided to leave the necessary code for making the adaptation of the GetUrlForLink easy for future helpers. That’s why I’ve also made the method receive a fragment, even though the main helper doesn’t accept it (as an exercise, you can always add more overloads which use those parameters: the private methods support them so it’s only necessary to add more overloads of the ActionLinkWithImage that receives them). A fragment is always in the form of http://…..#fragment. That’s why you’ve got that String.Concat there for joining an eventual fragment with the current url:

if( !String.IsNullOrEmpty( fragment ) ) {
            url += String.Concat (“#”, fragment);
}

And that’s it for the GetUrlForLink method. Getting the HTML for the inner image is also easy, as you can see by looking at the GetImageHtml method. We start by creating an UrlHelper object for getting the  correct url for the image. The main difference (when compared with the previous snippet) is that, in this case, we’re passing a relative url and so we’re using the Content method:

var imageUrl = new UrlHelper( helper.ViewContext.RequestContext ).Content( relativeImageUrl );

The “hard work” of getting the HTML is done by the TagBuilder class. Notice that I’m simply using the MergeAttribute and MergeAttributes method in order to set the attributes applied to the final IMG tag. The HTML is obtained by calling an overload of the ToString method and passing a value from the TagRenderMode enumeration to generate an image on the form <img … />:

var imageTag = new TagBuilder( "img" );
imageTag.MergeAttribute( "src", imageUrl, true );
imageTag.MergeAttributes( new RouteValueDictionary( imageHtmlAttributes ), true );
return imageTag.ToString( TagRenderMode.SelfCl

osing );

After having the inner HTML and the correct link url, we delegate the remaining work to the BuildTagForLink method:

private static TagBuilder BuildTagForLink( HtmlHelper htmlHelper,
                                               String imageHtml,
                                               Object linkHtmlAttributes,
                                               String url ) {
        var tagBuilder = new TagBuilder( "a" ) {
                                     InnerHtml = imageHtml
                         };
        tagBuilder.MergeAttributes( new RouteValueDictionary( linkHtmlAttributes ) );
        tagBuilder.MergeAttribute( "href", url );
        return tagBuilder;
}

As you can see, most of the work is done (once  again) by the TagBuilder class. Since we’re passing HTML for the link’s content, we just pass that value to the InnerHtml property without encoding it (as happens with the current Link extension methods). And that’s all that is needed for having a helper that generates HTML. As I’ve said, this is only demo code. In a real work extension method you’re expected to provide more overloads and perform more validations. I’ll leave those modifications for those that intend to make this a “real world” extension method helper.

Keep tuned for more on the ASP.NET MVC.

~ by Luis Abreu on February 2, 2009.

11 Responses to “The MVC platform: creating a new HTML helper”

  1. can you show the output rendered to the view so we can conceptually see the end result in View Source?

  2. ??? ??? ??????????? ?????,??????? ?????? +? ????,????? ??????????? ?????????,???? ?????? ??????.

  3. ??? ??? ??????????? ????? mp3,??????????? ????? ???????,??????????? ????? +??? ???????????,??????? ?????? ?????.

  4. gRzYRJ yozkqtcfostr, [url=http://uymdkemgtqfu.com/]uymdkemgtqfu[/url], [link=http://iejxmecsyxhe.com/]iejxmecsyxhe[/link], http://dzpqkmeleaok.com/

  5. yoo. bookmarked ))

  6. well.. it”s like I knew!

  7. hmm.. love this thoughts ))

  8. understanding sports betting lines football bet tips sports betting lines ncaa
    sports betting africa uganda bets wimbledon ufc 88 betting odds
    free bets offer sportsbook online betting how to bet on sports games most popular sports bets
    online bet boxing john morrison sports betting champ ufc 69 betting odds
    champions league betting picks sports bet lines melbourne cup online bet sports bet football
    sports betting in oregon sports betting nfl lines online poker sports betting
    reading baseball betting lines football betting websites sports betting manuals betting odds melbourne cup
    sport bet review bet on football sport betting uk
    sports bet spread sports betting mathematics live tennis betting soccer betting strategies
    teaser sports bet professional football betting odds sports betting in australia
    arc de triomphe betting odds live betting football financial betting sports betting odds tennis
    sports betting champ rapidshare 888 free bet sports betting in ontario

  9. ?? ???????? ? ???????. ?????? ? ??? ???? 747067

  10. ??? ???????: ???????.

    ???? ?????????? ?? ??? ?????? ? ????? – ???? ????????????? ??? ???????????
    ???? ?????????? ??? ?????????? – ????? ????????????? ???? ??????????
    ? ????????? ???? ????? ? ?????? ???????? ????? ???? ? ????????? ? ??????????
    ????????? ???? ? ???????? ? ?????? – ???? ? ???????? 2007 2009
    ????? ????????? ?? ???? ????? ??????: ????? ????????? ??? ?????
    ??? ????????????? ? ??????? ???????? – ??? ????????????? ? ???????????
    ?????????? ? ????????: ????????????? ???? ?????????? ? ????????
    ????? ???????????? ??????????? ?????? ????????????
    ????? ???? ? ?????? ????????? ???????? ????? ??????????
    ???? ????? ? ?????????? ???? ????? ? ???????
    ????? ????? ? ??????? ????????? ?? ?????????? ????? ?????
    ????????? ?????????? ??? ????? ???????: ?????????? ??? ????? 16
    ?????????? ?????? ????????????? ???? – ????????????? ???? ??? ???????
    ???????? ???? ????????? ??? ????? ??????: ???? ????????? ??? ?????????? ?????
    ???????????? ? ????? ????????: ???? ????????????? ? ??????????
    ???? ?????????? ?? 24lux ru ? ??????? – ?????????? ????? ??????????
    ???? ????? ? ??????? ?????? – ???? ????? ? ?????????
    ? ???????????? ??? ????????????? ? ????????: ??? ????? ????????????? ? ???????????? ? ??????
    ?????? ??????? ????? ?????????????: ?????? ??????? ????? ????????????? ? ??????

  11. ehh. good text ))

Leave a reply to Anonymous Cancel reply