How to use MVC 3 @Html.ActionLink inside c# code
Asked Answered
I

5

16

I want to call the @Html.ActionLink method inside a c# function to return a string with a link on it.

Something like this:

string a = "Email is locked, click " + @Html.ActionLink("here to unlock.", "unlock") ;
Iowa answered 30/11, 2011 at 18:33 Comment(1)
What I want is to add "ModelState.AddModelError" with link on it to resolve the errors.Iowa
I
8

Assuming that you want to accomplish this in your controller, there are several hoops to jump through. You must instantiate a ViewDataDictionary and a TempDataDictionary. Then you need to take the ControllerContext and create an IView. Finally, you are ready to create your HtmlHelper using all of these elements (plus your RouteCollection).

Once you have done all of this, you can use LinkExtensions.ActionLink to create your custom link. In your view, you will need to use @Html.Raw() to display your links, to prevent them from being HTML encoded. Here is the necessary code:

var vdd = new ViewDataDictionary();
var tdd = new TempDataDictionary();
var controllerContext = this.ControllerContext;
var view = new RazorView(controllerContext, "/", "/", false, null);
var html = new HtmlHelper(new ViewContext(controllerContext, view, vdd, tdd, new StringWriter()),
     new ViewDataContainer(vdd), RouteTable.Routes);
var a = "Email is locked, click " + LinkExtensions.ActionLink(html, "here to unlock.", "unlock", "controller").ToString();

Having shown all of this, I will caution you that it is a much better idea to do this in your view. Add the error and other information to your ViewModel, then code your view to create the link. If this is needed across multiple views, create an HtmlHelper to do the link creation.

UPDATE

To address one.beat.consumer, my initial answer was an example of what is possible. If the developer needs to reuse this technique, the complexity can be hidden in a static helper, like so:

public static class ControllerHtml
{
    // this class from internal TemplateHelpers class in System.Web.Mvc namespace
    private class ViewDataContainer : IViewDataContainer
    {
        public ViewDataContainer(ViewDataDictionary viewData)
        {
            ViewData = viewData;
        }

        public ViewDataDictionary ViewData { get; set; }
    }

    private static HtmlHelper htmlHelper;

    public static HtmlHelper Html(Controller controller)
    {
        if (htmlHelper == null)
        {
            var vdd = new ViewDataDictionary();
            var tdd = new TempDataDictionary();
            var controllerContext = controller.ControllerContext;
            var view = new RazorView(controllerContext, "/", "/", false, null);
            htmlHelper = new HtmlHelper(new ViewContext(controllerContext, view, vdd, tdd, new StringWriter()),
                 new ViewDataContainer(vdd), RouteTable.Routes);
        }
        return htmlHelper;
    }

    public static HtmlHelper Html(Controller controller, object model)
    {
        if (htmlHelper == null || htmlHelper.ViewData.Model == null || !htmlHelper.ViewData.Model.Equals(model))
        {
            var vdd = new ViewDataDictionary();
            vdd.Model = model;
            var tdd = new TempDataDictionary();
            var controllerContext = controller.ControllerContext;
            var view = new RazorView(controllerContext, "/", "/", false, null);
            htmlHelper = new HtmlHelper(new ViewContext(controllerContext, view, vdd, tdd, new StringWriter()),
                 new ViewDataContainer(vdd), RouteTable.Routes);
        }
        return htmlHelper;
    }
}

Then, in a controller, it is used like so:

var a = "Email is locked, click " + 
    ControllerHtml.Html(this).ActionLink("here to unlock.", "unlock", "controller").ToString();

or like so:

var model = new MyModel();
var text = ControllerHtml.Html(this, model).EditorForModel();

While it is easier to use Url.Action, this now extends into a powerful tool to generate any mark-up within a controller using all of the HtmlHelpers (with full Intellisense).

Possibilities of use include generating mark-up using models and Editor templates for emails, pdf generation, on-line document delivery, etc.

Interplead answered 30/11, 2011 at 19:49 Comment(4)
counsellorben, @roncansan... please seem my edited answer. I think this is entirely unecessary.Viscacha
An extensive solution and well written code. I'm not criticizing that at all; I just don't consider the controller a proper place to "generate markup". Perhaps too purist, but the controllers role is to handle route calls, gather necessary resources (models, services) and inject them into a view. The View's primary purpose is to template models. I don't think controllers should be used for presentation layer functionality, unless there are no other options. Can you think of a scenario where you would NEED to keep markup in the controller?Viscacha
"Can you think of a scenario where you would NEED to keep markup in the controller" How about a localized validation message that contains a link to another resource?" e.g. "An error occurred please click {0} to fix this.". @tvanfosson answer probably makes most sense though.Unabridged
I don't see a constructor for new ViewDataDictionary(vdd). It looks like it's any class that implements IViewDataContainer, but is there a class in .NET that implements it? It's a simple enough class if you want to build it on your own class ViewDataContainer : IViewDataContainer { public ViewDataDictionary ViewData { get; set; } }Kaitlin
W
6

You could create an HtmlHelper extension method:

public static string GetUnlockText(this HtmlHelper helper)
{
    string a = "Email is locked, click " + helper.ActionLink("here to unlock.", "unlock");
    return a;
}

or if you mean to generate this link outside of the scope of an aspx page you'll need to create a reference to an HtmlHelper and then generate. I do this in a UrlUtility static class (I know, people hate static classes and the word Utility, but try to focus). Overload as necessary:

public static string ActionLink(string linkText, string actionName, string controllerName)
{
    var httpContext = new HttpContextWrapper(System.Web.HttpContext.Current);
    var requestContext = new RequestContext(httpContext, new RouteData());
    var urlHelper = new UrlHelper(requestContext);

    return urlHelper.ActionLink(linkText, actionName, controllerName, null);
}

Then you can write the following from wherever your heart desires:

string a = "Email is locked, click " + UrlUtility.ActionLink("here to unlock.", "unlock", "controller");
Wilmington answered 30/11, 2011 at 18:37 Comment(2)
This should be the accepted answer...because the other answers don't allow your link to be generated in the Model...Puss
When I use the ActionLink function above in a static class, I get this error: 'UrlHelper' does not contain a definition for 'ActionLink' and no accessible extension method 'ActionLink' accepting a first argument of type 'UrlHelper' could be found.Conclude
V
6

There's a couple things bad about these other answers...

Shark's answer requires you to bring the LinkExtensions namespace into C#, which is not wrong, but undesirable to me.

Hunter's idea of making a helper is a better one, but still writing a helper function for a single URL is cumbersome. You could write a helper to help you build strings that accepted parameters, or you could simply do it the old fashion way:

var link = "Email is... click, <a href=\"" + Url.Action("action") + "\"> to unlock.</a>";

@counsellorben,

i see no reason for the complexity; the user wants only to render an Action's routing into a hard string containing an anchor tag. Moreover, ActionLink() in a hard-written concatenated string buys one nothing, and forces the developer to use LinkExtensions whidh are intended for Views.

If the user is diehard about using ActionLink() or does not need (for some reason) to calculate this string in the constructor, doing so in a view is much better.

I still stand by and recommend the answers tvanfosson and I provided.

Viscacha answered 30/11, 2011 at 18:46 Comment(0)
F
4

Your best bet is to construct the link manually using the UrlHelper available in the controller. Having said that, I'm suspicious that there is probably a better way to handle this in a view or partial view, shared or otherwise.

string a = "Email is locked, click <a href=\""
              + Url.Action( "unlock" )
              + "\">here to unlock.</a>";
Follicle answered 30/11, 2011 at 18:42 Comment(0)
P
1

Maybe try this:

string a = "Email is locked, click " + System.Web.Mvc.Html.LinkExtensions.ActionLink("here to unlock.", "unlock");
Pause answered 30/11, 2011 at 18:37 Comment(2)
If I call the function ActionLink like this, I need to pass 3 parameters , the first should be of type (this HtmlHelper htmlHelper), second linkText, third controller. On the first parameter I tried, null and create a new HtmlHelper without success.Iowa
This Extension method need HtmlHelper instanceGownsman

© 2022 - 2024 — McMap. All rights reserved.