RenderPartial to String in ASP.NET MVC

October 12th, 2009 by Kevin

About a year ago I released some code that allowed you to render a partial view to a string inside your controllers in ASP.NET MVC (beta, at the time). While this solution worked, it required a whole bunch of code to achieve a fairly simple goal. Well, a lot has changed in the MVC world, and rendering a partial view to string has, thankfully, become much, much easier. (Thanks to Martin where I originally got this code from!)

Below is the code I am now using to create my RenderPartialToString method:

/// Static Method to render string - put somewhere of your choosing
public static string RenderPartialToString(string controlName, object viewData)
{
     ViewDataDictionary vd = new ViewDataDictionary(viewData);
     ViewPage vp = new ViewPage { ViewData = vd };
     Control control = vp.LoadControl(controlName);

     vp.Controls.Add(control);

     StringBuilder sb = new StringBuilder();
     using (StringWriter sw = new StringWriter(sb))
     {
         using (HtmlTextWriter tw = new HtmlTextWriter(sw))
         {
             vp.RenderControl(tw);
         }
     }

     return sb.ToString();
}

As you can see, this method is much shorter and doesn’t have any sort of additional library contingencies. Pass it a path reference to your partial view and ViewData object (maybe you want that, maybe you don’t) and away you go. The following is an example of how to call the above method from your controller:

// Controller Method
public string Create()
{
     {...}
     var viewData = new SomeViewData { Note = n };

     string s = RenderPartialToString("~/Views/Clients/Notes/ClientNotesRow.ascx", viewData);

     return s;
}

That’s all there is to it!

Dan Atkinson October 13th, 2009 at 5:23 am


You link from your last blog post, but this isn’t the same as rendering a view to a string.

This is just rendering a partial as views don’t extend Control.

So, if you wanted to actually generate an entire view into a string, you’d still need to use the old method. That or rework your views to be partials instead…

Kevin October 13th, 2009 at 9:01 am


Hey Dan,

As far as I know, your point is quite valid. If you want to render an entire view to a string, the previous post will work and this one will not.

However, the idea behind RenderPartialToString, is geared at just that, rendering a partial view to a string.

Brian October 27th, 2009 at 11:01 pm


When I tried to use this code I had to change this line:

ViewDataDictionary vd = new ViewDataDictionary(viewData);

To:

ViewPage vp = new ViewPage { ViewData = (ViewDataDictionary)viewData };

Not sure why but when I created the ViewDataDictionary object with the constructor I was losing all of viewData’s parameters.

Gary Davis December 4th, 2009 at 10:05 am


This worked nicely except when the ascx control used an Html helper (). Html was null causing a Null-ref trap.

The trap did not occur if the control was referenced by Html.RenderPartial() instead of RenderPartialToString().

Gary

Gary Davis December 4th, 2009 at 10:37 am


Based on what I read in your reference, I made these changes to solve the trap:


public static string RenderPartialToString(this HtmlHelper htmlHelper, string controlName, object viewData)
{
// Added Url to allow ascx to use Url.Action()
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
// Added ViewContext to allow ascx to use Html helpers
var vp = new ViewPage { ViewData = vd, ViewContext = htmlHelper.ViewContext, Url = urlHelper};

Note this is an Html helper called with Html.RenderPartialToString(). If not, use these changes:


var vc = new ViewContext();
// Added Url to allow ascx to use Url.Action()
var urlHelper = new UrlHelper(vc.RequestContext);
// Added ViewContext to allow ascx to use Html helpers
var vp = new ViewPage { ViewData = vd, ViewContext = vc, Url = urlHelper };

Gary

Bobasoft January 9th, 2010 at 4:23 am


when use ajax helper in partial view – will be crash.

http://msug.vn.ua/blogs/bobasoft/archive/2010/01/07/render-partialview-to-string-asp-net-mvc.aspx – nicer solution

Joshua March 25th, 2010 at 9:24 am


I have implemented this solution, however, I am curious how we retrieve the data in the partial view?

Currently, I am casting the ViewData Objects in the partial view (not using a view model)

Unfortunately, ViewData seems to be empty when rendering.. No matter what I pass into RenderPartialToString as the viewData…

NE thoughts?

Kevin Craft May 15th, 2010 at 11:11 am


I wrote a post that solves the same problem in a slightly different way. I dug into the ASP.NET MVC source and used the method they use. The Html and Ajax helpers work properly. You also don’t have to provide the full path to the partial view’s filename. You can use it just like the “PartialView” function in the controller. Check it out at http://craftycode.wordpress.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/

Wessel August 20th, 2010 at 3:36 am


The solution is very simple.

Create a new Viewclass based on the WebFormView, and use that as the ViewEnging for rendering (by returning this View in the CreatePartialView and CreateView methods of a custom WebFormViewEngine).

Override the Render method in your new Viewclass and create a new TextWriter.
Store the viewContent.Writer and set the viewContent.Writer to the newly created TextWriter.
Call MyBase.Render(viewContext,writer).
The writer is ignored, but you just changed the writer of the viewContext so any output goes there and not to the Response!

Restore the viewContext.Writer.

You can now use the content of the newly created TextWriter and do anything you like.
Finally write the changed content to the old viewContent.Writer (which writes to Response).

Performance does not degrade as far as I can tell and this works for partialrendering as well as for normal pages.
Basically for all view rendered output.

Example:

Imports System.IO

Public Class CustomRenderView
Inherits WebFormView

Public Sub New(ByVal viewPath As String)
MyBase.New(viewPath)
End Sub

Public Sub New(ByVal viewPath As String, ByVal masterPath As String)
MyBase.New(viewPath, masterPath)
End Sub

Public Overrides Sub Render(ByVal viewContext As System.Web.Mvc.ViewContext, ByVal writer As System.IO.TextWriter)
Dim newWriter As TextWriter = New StringWriter()

Dim oldWriter As TextWriter = viewContext.Writer
viewContext.Writer = newWriter

MyBase.Render(viewContext, newWriter) ‘use newWriter in case MS changes its mind to ignore it…
viewContext.Writer = oldWriter

Dim pageSource As String = newWriter.ToString()
‘do something with the generated output in pageSource

‘write changed output to the original viewContext.Writer (goes to Response object)
oldWriter.Write(pageSource)
End Sub

End Class

That’s all folks!