21

I am trying to use the accepted answer from this question.

It seems that it will be exactly what i am looking for, but i have a problem. I don't know how to actually call it. This is what i have so far:

First i am copying the code from the solution i mentioned:

public string ToHtml(string viewToRender, ViewDataDictionary viewData, ControllerContext controllerContext)
{
    var result = ViewEngines.Engines.FindView(controllerContext, viewToRender, null);

    StringWriter output;
    using (output = new StringWriter())
    {
        var viewContext = new ViewContext(controllerContext, result.View, viewData, controllerContext.Controller.TempData, output);
        result.View.Render(viewContext, output);
        result.ViewEngine.ReleaseView(controllerContext, result.View);
    }

    return output.ToString();
}

This is what i have:

string viewToRender = "...";
int Data1 = ...;
int Data2 = ...;

System.Web.Mvc.ViewDataDictionary viewData = new System.Web.Mvc.ViewDataDictionary();
viewData.Add("Data1",Data1);
viewData.Add("Data2",Data2);

string html = ToHtml(viewToRender, viewData, ?????)//Here is my problem.

What should i pass in the controllerContext parameter?

Community
  • 1
  • 1
Giannis Paraskevopoulos
  • 18,261
  • 1
  • 49
  • 69
  • The controller context is a property of a MVC controller. If you want to get a view of the current Controller you type: this.ControllerContext – Oliver Aug 26 '13 at 11:04
  • @Oliver I want to call this method outside of a controller. Is this even possible? If it is, then i would like to define the the controller somehow. How do i do this? – Giannis Paraskevopoulos Aug 26 '13 at 11:07
  • I use this helper: public static string RenderPartialViewToString(Controller controller, string viewName, object model) and than I just call controller.ControllerContext. – Oliver Aug 26 '13 at 11:17
  • Have you tried to create a new instance of you Controller and than pass the controllerContext to your helper? – Oliver Aug 26 '13 at 11:18
  • @Oliver Thanks for the info. If i create a new object it works... Is it possible that i pass it as a string as i do in the ViewToRender? Do i have to use reflection? – Giannis Paraskevopoulos Aug 26 '13 at 11:54
  • If you pass the controller name as a string you would have to use reflection. If you use a DI Framework like Unity or Castle they could resolve the controller for you (thats what I would prefer) – Oliver Aug 26 '13 at 12:32
  • possible duplicate of [Render a view as a string](http://stackoverflow.com/questions/483091/render-a-view-as-a-string) – Liam Jul 24 '15 at 08:21

3 Answers3

43

Rather than inherit Controller which means you have to remember to implement this every time, or inherit from a CustomControllerBase, which means you have to remember to inherit every time - simply make an extension method:

public static class ControllerExtensions
{
    public static string RenderView(this Controller controller, string viewName, object model)
    {
        return RenderView(controller, viewName, new ViewDataDictionary(model));
    }

    public static string RenderView(this Controller controller, string viewName, ViewDataDictionary viewData)
    {
        var controllerContext = controller.ControllerContext;

        var viewResult = ViewEngines.Engines.FindView(controllerContext, viewName, null);

        StringWriter stringWriter;

        using (stringWriter = new StringWriter())
        {
            var viewContext = new ViewContext(
                controllerContext,
                viewResult.View,
                viewData,
                controllerContext.Controller.TempData,
                stringWriter);

            viewResult.View.Render(viewContext, stringWriter);
            viewResult.ViewEngine.ReleaseView(controllerContext, viewResult.View);
        }

        return stringWriter.ToString();
    }
}

Then within your Controller you can call like this:

this.RenderView("ViewName", model);
dav_i
  • 27,509
  • 17
  • 104
  • 136
  • Nice one I like your approch. Thx mate – Ali Jan 03 '14 at 06:49
  • 3
    And for anyone looking for a partial view, replace FindView with FindPartialView. – RitchieD Jul 21 '15 at 15:51
  • 1
    How you set viewData? For me, it is null, which throws an error. – FrenkyB Apr 08 '17 at 11:12
  • Is it possible to call controller action which returns view instead of calling view directly? – FrenkyB Apr 10 '17 at 03:52
  • 1
    @FrenkyB Instead of viewData you can insert controllerContext.Controller.ViewData – Rubenisme May 02 '17 at 13:26
  • @FrenkyB I tried to edit the answer but unfortunately the reviewers can't see that this does not work for a lot of things, and since I can't send dav_i messages to edit his post I will post a new one. Check my answer for strongly typed model and partial view. – Rubenisme May 03 '17 at 08:53
  • How do I pass `ViewBag` to the extension method? Or is it already included in `controllerContext.Controller.TempData`? – joym8 Nov 13 '17 at 21:10
12

You can create a base controller which obviously extends a controller and use above function in the base controller and other controller which extends this base controller will be able to use it. However the ControllerContext must be used as

Request.RequestContext

And Hence your BaseController will be like

public class BaseController: Controller
{
//your function here
}

And your ToHtml() function will be

protected virtual string ToHtml(string viewToRender, ViewDataDictionary viewData )
{
   var controllerContext=Request.RequestContext;
   var result = ViewEngines.Engines.FindView(controllerContext, viewToRender, null);

   StringWriter output;
   using (output = new StringWriter())
   {
      var viewContext = new ViewContext(controllerContext, result.View, viewData, controllerContext.Controller.TempData, output);
      result.View.Render(viewContext, output);
      result.ViewEngine.ReleaseView(controllerContext, result.View);
   }

   return output.ToString();
}

And on using the base controller

public class MyController: BaseController
{
//ToHtml(...);
}
Amar Palsapure
  • 9,590
  • 1
  • 27
  • 46
serene
  • 685
  • 6
  • 16
  • Thanks! Do you know, how I can add `Model` object to render view? – dima_horror Jan 10 '14 at 10:52
  • 1
    I found :) `ViewData.Model = obj;`. – dima_horror Jan 10 '14 at 10:56
  • What to send in viewdata? I'm not getting it. – Code Rider May 04 '16 at 04:45
  • @CodeRider you can send any object, model, primitive value in ViewData from controller and get its value in view. Make sure you use the SAME key to set and get the value, else Null Exception will occur. – serene May 04 '16 at 08:21
  • Is it possible to specify the controller? Let's say I'm making the request from "Controller 1", but need to retrieve an Action/Result from "Controller 2". (And both controllers inherit the base controller) Is this possible? – Penjimon Jan 11 '19 at 00:01
4

This is pretty much a copy of dav_i's post except that you have a model with strong typing and also the ability of producing partial views:

Rather than inherit Controller which means you have to remember to implement this every time, or inherit from a CustomControllerBase, which means you have to remember to inherit every time - simply make an extension method:

public static class ControllerExtensions
{
    public static string RenderView<TModel>(this Controller controller, string viewName, TModel model, bool partial = false)
    {
        var controllerContext = controller.ControllerContext;
        controllerContext.Controller.ViewData.Model = model;

        // To be or not to be (partial)
        var viewResult = partial ? ViewEngines.Engines.FindPartialView(controllerContext, viewName) : ViewEngines.Engines.FindView(controllerContext, viewName, null);

        StringWriter stringWriter;

        using (stringWriter = new StringWriter())
        {
            var viewContext = new ViewContext(
                controllerContext,
                viewResult.View,
                controllerContext.Controller.ViewData,
                controllerContext.Controller.TempData,
                stringWriter);

            viewResult.View.Render(viewContext, stringWriter);
            viewResult.ViewEngine.ReleaseView(controllerContext, viewResult.View);
        }

        return stringWriter.ToString();
    }
}

Then within your Controller you can call like this (for a full view):

this.RenderView("ViewName", model);

That means you will get the doctype and the HTML element etc too. For partial view use:

this.RenderView("ViewName", model, true);
Community
  • 1
  • 1
Rubenisme
  • 787
  • 1
  • 8
  • 15