Gil
Gil

Reputation: 1047

Using HtmlHelper in a Controller

Is it possible to use HtmlHelper in a controller, for-example to get the TextBox(...) method? not that I can't write the html that it generates myself, but I just want to understand how this works so I can create the best solution.

Upvotes: 72

Views: 63053

Answers (7)

jonmeyer
jonmeyer

Reputation: 848

For .NET Core 2 MVC: https://github.com/aspnet/Mvc/issues/7321

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.Options;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;


    public class HelperGenerator
    {
        private readonly IHtmlGenerator _htmlGenerator;
        private readonly ICompositeViewEngine _compositeViewEngine;
        private readonly IModelMetadataProvider _modelMetadataProvider;
        private readonly IViewBufferScope _viewBufferScope;
        private readonly IActionContextAccessor _actionContextAccessor;
        private readonly HtmlHelperOptions _htmlHelperOptions;

        public HelperGenerator(IHtmlGenerator htmlGenerator, ICompositeViewEngine compositeViewEngine, IModelMetadataProvider modelMetadataProvider, IViewBufferScope viewBufferScope, IActionContextAccessor actionContextAccessor, IOptions<MvcViewOptions> options)
        {
            _htmlGenerator = htmlGenerator;
            _compositeViewEngine = compositeViewEngine;
            _modelMetadataProvider = modelMetadataProvider;
            _viewBufferScope = viewBufferScope;
            _actionContextAccessor = actionContextAccessor;
            _htmlHelperOptions = options.Value.HtmlHelperOptions;
        }
        public IHtmlHelper HtmlHelper(ViewDataDictionary ViewData, ITempDataDictionary TempData)
        {
            var helper = new HtmlHelper(_htmlGenerator, _compositeViewEngine, _modelMetadataProvider, _viewBufferScope, HtmlEncoder.Default, UrlEncoder.Default);
            var viewContext = new ViewContext(_actionContextAccessor.ActionContext,
               new FakeView(),
               ViewData,
               TempData,
               TextWriter.Null,
               _htmlHelperOptions);
            helper.Contextualize(viewContext);
            return helper;
        }
        private class FakeView : IView
        {
            public string Path => "View";

            public Task RenderAsync(ViewContext context)
            {
                return Task.FromResult(0);
            }
        }
    }

Make sure to register in services:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

Upvotes: 3

Hossein Hajizadeh
Hossein Hajizadeh

Reputation: 1495

  • using System.Web.Mvc;
  • using System.Web.Mvc.Html;

     HtmlHelper helper = new HtmlHelper(new ViewContext(ControllerContext, new            WebFormView(ControllerContext, "Index"), new ViewDataDictionary(), new TempDataDictionary(), new System.IO.StringWriter()), new ViewPage());
    

Upvotes: 0

Nicholas Petersen
Nicholas Petersen

Reputation: 9578

If someone is trying to do this from without a Controller (like when unit testing), there are further problems to deal with, as many of these methods (which I know, were not a testing scenario, but for that scenario) were throwing Null exceptions (ViewContext.ScopeCache). You can see this by the following (note all of these ways require a ViewContext instance to be formed, which is one of the parameters you insert into the constructor of the HtmlHelper instance, so on that object):

viewContext.UnobtrusiveJavaScriptEnabled = false;

Simply setting that value throws an exception with many of these methods, but the problem was fixed for me by this answer, see how he gets an HtmlHelper (see also here).

Upvotes: 0

Anubis
Anubis

Reputation: 2624

using System.Web.Mvc;
using System.Web.Mvc.Html;

var h = new HtmlHelper<Effort>(new ViewContext(ControllerContext, new WebFormView(ControllerContext, "omg"), new ViewDataDictionary(), new TempDataDictionary(), new StringWriter()), new ViewPage());

h.DisplayFor(e => Model.Efforts[i].Content.Offer.Price1.Value)

Upvotes: 6

Vitaliy Ulantikov
Vitaliy Ulantikov

Reputation: 10534

You can use method like this:

public static HtmlHelper GetHtmlHelper(this Controller controller)
{
    var viewContext = new ViewContext(controller.ControllerContext, new FakeView(), controller.ViewData, controller.TempData, TextWriter.Null);
    return new HtmlHelper(viewContext, new ViewPage());
}

public class FakeView : IView
{
    public void Render(ViewContext viewContext, TextWriter writer)
    {
        throw new InvalidOperationException();
    }
}

Upvotes: 32

Richard
Richard

Reputation: 22036

The HtmlHelper is part of the View mechanism by design and should be considered separate to the Controller and Model parts of MVC. I am not sure why you would want to generate controls inside the controller as it's role is to deliver the Data to the view for rendering.

I am not saying that you cannot achieve it, but for good design it would be better.

Can you explain what you are trying to achieve and then we could look at doing it in an "MVC way"?

Upvotes: 9

Mauricio Scheffer
Mauricio Scheffer

Reputation: 99750

Here's an example adapted from this:

var h = new HtmlHelper(new ViewContext(ControllerContext, new WebFormView("omg"), new ViewDataDictionary(), new TempDataDictionary()), new ViewPage());
h.TextBox("myname");

Note that this is a hack, it can be done but I don't think there's any good reason to do this...

Upvotes: 47

Related Questions