170

Normally in an ASP.NET view one could use the following function to obtain a URL (not an <a>):

Url.Action("Action", "Controller");

However, I cannot find how to do it from a custom HTML helper. I have

public class MyCustomHelper
{
   public static string ExtensionMethod(this HtmlHelper helper)
   {
   }
}

The helper variable has the Action and GenerateLink methods, but they generate <a>’s. I did some digging in the ASP.NET MVC source code, but I could not find a straightforward way.

The problem is that the Url above is a member of the view class and for its instantiation it needs some contexts and route maps (which I don’t want to be dealing with and I’m not supposed to anyway). Alternatively, the instance of the HtmlHelper class has also some context which I assume is either supper of subset of the context information of the Url instance (but again I don’t want to dealing with it).

In summary, I think it is possible but since all ways I could see, involve some manipulation with some more or less internal ASP.NET stuff, I wonder whether there is a better way.

Edit: For instance, one possibility I see would be:

public class MyCustomHelper
{
    public static string ExtensionMethod(this HtmlHelper helper)
    {
        UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
        urlHelper.Action("Action", "Controller");
    }
}

But it does not seem right. I don't want to be dealing with instances of UrlHelper myself. There must be an easier way.

Jan Zich
  • 14,993
  • 18
  • 61
  • 73
  • 3
    I realize this is a simplified example, but for the example shown I would extend UrlHelper instead of HtmlHelper. Your real code may need both, though. – Craig Stuntz Sep 18 '09 at 12:55
  • Sorry, I should have been more clear: I wanted to do some HTML rendeting in the extension method and I needed to generate URL's for it. – Jan Zich Sep 18 '09 at 13:49

3 Answers3

220

You can create url helper like this inside html helper extension method:

var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var url = urlHelper.Action("Home", "Index")
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 2
    I think that it would be better if constructor also initialize RouteCollection `new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection)` – kpull1 Sep 23 '15 at 07:33
22

You can also get links using UrlHelper public and static class:

UrlHelper.GenerateUrl(null, actionName, controllerName, null, null, null, routeValues, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true)

In this example you don't have to create new UrlHelper class what could be a little advantage.

cryss
  • 4,130
  • 1
  • 29
  • 34
10

Here is my tiny extenstion method for getting UrlHelper of a HtmlHelper instance :

  public static partial class UrlHelperExtensions
    {
        /// <summary>
        /// Gets UrlHelper for the HtmlHelper.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <returns></returns>
        public static UrlHelper UrlHelper(this HtmlHelper htmlHelper)
        {
            if (htmlHelper.ViewContext.Controller is Controller)
                return ((Controller)htmlHelper.ViewContext.Controller).Url;

            const string itemKey = "HtmlHelper_UrlHelper";

            if (htmlHelper.ViewContext.HttpContext.Items[itemKey] == null)
                htmlHelper.ViewContext.HttpContext.Items[itemKey] = new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection);

            return (UrlHelper)htmlHelper.ViewContext.HttpContext.Items[itemKey];
        }
    }

Use it as:

public static MvcHtmlString RenderManagePrintLink(this HtmlHelper helper, )
{    
    var url = htmlHelper.UrlHelper().RouteUrl('routeName');
    //...
}

(I'm posting this ans for reference only)

Kibria
  • 1,865
  • 1
  • 15
  • 17
  • Excellent approach because it reuses an existing object rather than creating a new one. – Mike Mar 11 '17 at 05:04
  • We are using ASP.NET 4.5 and were experiencing re-entrancy issues. We don't believe UrlHelper is reusable across http requests. Please take notice. – Carl in 't Veld Jul 02 '18 at 15:58