7

So basically I have a partial view which can build a nice table for me. I would like to email this table out every week to my users. Instead of having to basically copy the template again, I would like to forward my model to the controller and receive the corresponding generated HTML as a String.

Is it possible to do this in a Controller, I feel it should be a pretty simple process.

Dr1Ku
  • 2,875
  • 3
  • 47
  • 56
Steve
  • 2,971
  • 7
  • 38
  • 63
  • Added a tag for asp.net - this is asp.net, right? It's really helpful if askers tag questions with the platform or technology they're using, so answerers can tell at a glance if this is something they can help with. I'm a Java guy, so i can't, but i had to come here, read the question, make a guess, and google before i knew that i wasn't going to be any use! – Tom Anderson Apr 03 '11 at 20:43
  • thanks sorry. wasnt really concentrating! yes this is asp.net mvc – Steve Apr 03 '11 at 20:45
  • http://stackoverflow.com/questions/483091/render-a-view-as-a-string/2759898#2759898 – dotjoe Apr 04 '11 at 23:56

4 Answers4

22

Put this into a Helper file:

public static string RenderViewToString(ControllerContext context, string viewName, object model)
        {
            if (string.IsNullOrEmpty(viewName))
                viewName = context.RouteData.GetRequiredString("action");

            ViewDataDictionary viewData = new ViewDataDictionary(model);

            using (StringWriter sw = new StringWriter())
            {
                ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(context, viewName);
                ViewContext viewContext = new ViewContext(context, viewResult.View, viewData, new TempDataDictionary(), sw);
                viewResult.View.Render(viewContext, sw);

                return sw.GetStringBuilder().ToString();
            }
        }

And then from the controller you can call it like this:

var order = orderService.GetOrder(id);

var orderPrint = MyHelper.RenderViewToString(this.ControllerContext, "_OrderView", order);
Nestor
  • 1,969
  • 4
  • 25
  • 30
5

If you search for rendering partial views to strings you'll also come across some good leads. That's what I did to come up with the following extension method for the ControllerBase class:

public static string RenderPartialViewToString( this ControllerBase controller, string partialPath, ViewDataDictionary viewData = null )
{
    if( string.IsNullOrEmpty(partialPath) )
        partialPath = controller.ControllerContext.RouteData.GetRequiredString("action");

    using( StringWriter sw = new StringWriter() )
    {
        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialPath);

        ViewContext viewContext = new ViewContext(controller.ControllerContext,
            viewResult.View,
            ( viewData == null ) ? controller.ViewData : viewData,
            controller.TempData,
            sw);

        // copy retVal state items to the html helper 
        foreach( var item in viewContext.Controller.ViewData.ModelState )
        {
            if( !viewContext.ViewData.ModelState.Keys.Contains(item.Key) )
                viewContext.ViewData.ModelState.Add(item);
        }

        viewResult.View.Render(viewContext, sw);

        return sw.GetStringBuilder().ToString();
    }
}

Conceptually, the procedure to follow involves using the ViewEngines defined for your app to find a partial view by its name. You then create a ViewContext off of that partial, and copy the various model state properties over to it.

The code assigns an optional ViewDataDictionary that you can provide to the ViewContext. If you don't provide the ViewDataDictionary it grabs the ViewDataDictionary defined for the controller that it's being called against.

What this means is that you can either define ViewData values (or ViewBag properties) directly in your controller and then call the extension method -- which will apply those ViewData/ViewBag properties to the partial when it gets rendered -- or you can create a separate ViewDataDictionary object in your action method and pass it to the extension method. The first is quicker/easier, but it "pollutes" the ViewData for your action method, while the second takes a little longer to set up but lets you keep your partial view data separate from your action method's ViewData.

0

Look into the MvcMailer project. Coupled with a partial view that renders your table, you should be able to pretty easily put together emails with your tables.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • how could i go about using this if I wasn't going to email it out. Say maybe i wanted to pass the control down to the page? – Steve Apr 04 '11 at 12:28
  • @Steve - I suppose you'd use conditional logic to either render with the mailer or render a normal view. – tvanfosson Apr 04 '11 at 12:35
0

Render a view as a string

I use something simple like above but I almost always create separate views for the emails. Mainly due to the need to use absolute links and inserting the CSS into the head.

Community
  • 1
  • 1
dotjoe
  • 26,242
  • 5
  • 63
  • 77