Update! RenderPartial to String has become much easier in ASP.NET MVC, see the new post here!
We usually don’t post much about hardcore programming-related things here, but this is an exception to the rule.
We’re primarily a Ruby on Rails shop, but we still do ASP.NET development fairly regularly. Our overall interest in ASP.NET was waning until the new ASP.NET MVC framework was released. ASP.NET MVC brings a number of the concepts we love from Rails into the ASP.NET arena.
However, one limitation we’ve come across with ASP.NET MVC is the lack of ability to render a partial to a string. This is really handy if you’re doing Ajax things; it also happens to be one of the things we really love about Rails. Additionally, it’s a feature others have been wanting, too, see here and here.
Well, the good news is I have a solution for at least some of you out there. I’ve cobbled together a few concepts from various sources and forum posts to make it all happen. Here is how you can render a view into a string.
First, you need to include these files in your project, both are from the MvcContrib project.
Download BlockRender.cs – from MvcContrib
using System;
using System.IO;
using System.Web;
using System.Collections.Generic;
namespace MvcContrib.UI
{
/// <summary>Renders an Action delegate and captures all output to a string. </summary>
public class BlockRenderer
{
private readonly HttpContextBase _httpContext;
public BlockRenderer(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
public partial class HttpResponse
{
public bool UsingHttpWriter { get { return true; } }
}
/// <summary>Renders the action and returns a string.</summary>
/// <param name="viewRenderer">The delegate to render.</param>
/// <returns>The rendered text.</returns>
public string Capture(Action viewRenderer)
{
HttpResponseBase resp = _httpContext.Response;
Stream originalFilter = null;
CapturingResponseFilter innerFilter;
string capturedHtml = "";
if (viewRenderer != null)
{
try
{
resp.Flush();
originalFilter = resp.Filter;
innerFilter = new CapturingResponseFilter(resp.Filter);
resp.Filter = innerFilter;
viewRenderer();
resp.Flush();
capturedHtml = innerFilter.GetContents(resp.ContentEncoding);
}
finally
{
if (originalFilter != null)
{
resp.Filter = originalFilter;
}
}
}
return capturedHtml;
}
}
}
Download CapturingResponseFilter.cs – from MvcContrib
using System.IO;
using System.Text;
namespace MvcContrib.UI
{
public class CapturingResponseFilter : Stream
{
private Stream _sink;
private MemoryStream mem;
public CapturingResponseFilter(Stream sink)
{
_sink = sink;
mem = new MemoryStream();
}
// The following members of Stream must be overriden.
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return 0; }
}
public override long Position { get; set; }
public override long Seek(long offset, SeekOrigin direction)
{
return 0;
}
public override void SetLength(long length)
{
_sink.SetLength(length);
}
public override void Close()
{
_sink.Close();
mem.Close();
}
public override void Flush()
{
_sink.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _sink.Read(buffer, offset, count);
}
// Override the Write method to filter Response to a file.
public override void Write(byte[] buffer, int offset, int count)
{
//Here we will not write to the sink b/c we want to capture
//Write out the response to the file.
mem.Write(buffer, 0, count);
}
public string GetContents(Encoding enc)
{
var buffer = new byte[mem.Length];
mem.Position = 0;
mem.Read(buffer, 0, buffer.Length);
return enc.GetString(buffer, 0, buffer.Length);
}
}
}
With the above two files included in your project, you’re almost ready to go. Now you just need to include a method that makes all the magic happen (more easily). As seen in the example below, define the RenderPartialToString() and then call it from your controller:
/// Static Method to render string - put somewhere of your choosing
public static string RenderPartialToString(string userControl, object viewData, ControllerContext controllerContext)
{
HtmlHelper h = new HtmlHelper(new ViewContext(controllerContext, new WebFormView("omg"), null, null), new ViewPage());
var blockRenderer = new BlockRenderer(controllerContext.HttpContext);
string s = blockRenderer.Capture(
() => RenderPartialExtensions.RenderPartial(h, userControl, viewData)
);
return s;
}
/// Your Controller method...
public ActionResult MakeStringForMe()
{
var objectViewData = new objectViewData { SomeString = "Dude", SomeNumber = 1 };
string s = RenderPartialToString("~/Views/Controls/UserControl.ascx", objectViewData, this.ControllerContext);
View();
}
With a little bit of luck and good fortune, it should work and you’ll be rendering partials to strings like a machine!
How It All Works
Basically, it’s using the Response object to render a usercontrol and capture the output into a string. Then, it filters that rendered content out of the response with some fancy trickery. All in all, it’s definitely a hack, but it gets the job done—at least until the Microsoft team can address this issue at its core (hint hint).
A Few Limitiations and/or Questions
I read somewhere that this might not work if you need to retrieve things out of the session. I have not verified this.
Additionally, I have no idea what effects using this approach will have on the large-scale performance of an app. It hasn’t caused any problems for the application I’m using it on.
Lastly, you cannot specify the ContentType on the response. If you do, you’ll get an exception:
Server cannot set content type after HTTP headers have been sent
This seems to be a limitation that results from rendering a usercontrol. This will be problematic if you want your controller to return data with a ContenType of, say, “text/javascript.” I ran into this problem while building a Rails-like RJS framework (see below), but managed to deal with it fairly elegantly.
Rails-like RJS for ASP.NET MVC
In line with the ability to RenderPartial to string, I’ve also put together a Rails-like RJS framework for ASP.NET MVC. Simply put, RJS is a framework that renders and builds dynamic javascript on the server. Similar to Rails’ RJS, it’s specific to the Prototype framework – sorry JQuery guys (for what it’s worth, it could probably be easily ported to JQuery!).
Here’s a quick snippet of what this allows you to do from your Controller:
// Example Usage in your Controller:
public ActionResult HideElement(string elementId)
{
RjsResult r = new RjsResult();
r.Effect(elementId, RjsResult.EffectTypes.Hide);
return r;
}
// A more complicated but powerful example of returning the contents of two UserControls and inserting them into two different <div>'s
public ActionResult InsertUserControl()
{
ObjectViewData viewData = new ObjectViewData { SomeString = "dude", SomeNumber = 1 };
var r = new RjsResult();
r.Insert("div_one", RjsResult.Positions.Top, "~/Views/Controls/UserControl1.ascx", ObjectViewData, ControllerContext);
r.Insert("div_two", RjsResult.Positions.Bottom, "~/Views/Controls/UserControl2.ascx", ObjectViewData, ControllerContext);
return r;
}
In both of the above examples, I’m calling a few different methods on the RjsResult object. Behind the scenes, the RjsResult object is building up a string of javascript commands that make all the magic happen in the user’s browser. If you’re still confused, I suggest you read this page from the Rails documentation, which might explain the concept better.
With that said, here is the code for my ASP.NET MVC RJS framework!
Download RjsResult.cs
namespace System.Web.Mvc
{
using System;
using System.Text;
using System.Web;
using System.Collections.Generic;
using System.Security.Policy;
using System.Web.Script.Serialization;
using MvcContrib.UI;
using System.Web.Mvc.Html;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class RjsResult : ActionResult
{
#region Constructors
public RjsResult(bool setContentType)
{
Actions = new List<IRjsActionBase>();
this.SetContentType = setContentType;
}
public RjsResult() : this(false)
{
}
#endregion
#region Private Vars
private bool SetContentType { get; set; }
private List<IRjsActionBase> Actions
{
get;
set;
}
#endregion
#region Rjs Actions
public interface IRjsActionBase
{
string Render();
}
private class RjsUpdateAction : IRjsActionBase
{
public RjsUpdateAction(string element, string contents)
{
this.element = element;
this.contents = contents;
}
public string element { get; set; }
public string contents { get; set; }
#region IRjsActionBase Members
public string Render()
{
if (!contents.StartsWith("\""))
contents = "\"" + contents + "\"";
return string.Format("Element.update(\"{0}\", {1});", element, contents);
}
#endregion
}
private class RjsInsertAction : IRjsActionBase
{
public RjsInsertAction(string element, Positions p, string contents)
{
this.element = element;
this.contents = contents;
this.position = p;
}
public string element { get; set; }
public string contents { get; set; }
public Positions position { get; set; }
#region IRjsActionBase Members
public string Render()
{
if (!contents.StartsWith("\""))
contents = "\"" + contents + "\"";
return string.Format("Element.insert(\"{0}\", {{{1}: {2} }});", element, position, contents);
}
#endregion
}
private class RjsStringAction : IRjsActionBase
{
public string contents { get; set; }
public RjsStringAction(string contents)
{
this.contents = contents;
}
#region IRjsActionBase Members
public string Render()
{
return this.contents;
}
#endregion
}
private class RjsShowAction : IRjsActionBase
{
public RjsShowAction(string element)
{
this.element = element;
}
public string element { get; set; }
#region IRjsActionBase Members
public string Render()
{
return string.Format("$(\"{0}\").show();", element);
}
#endregion
}
private class RjsAlertAction : IRjsActionBase
{
public RjsAlertAction(string message)
{
this.message = message;
}
public string message { get; set; }
#region IRjsActionBase Members
public string Render()
{
return string.Format("alert('{0}');", message);
}
#endregion
}
private class RjsHideAction : IRjsActionBase
{
public RjsHideAction(string element)
{
this.element = element;
}
public string element { get; set; }
#region IRjsActionBase Members
public string Render()
{
return string.Format("$(\"{0}\").hide();", element);
}
#endregion
}
private class RjsRemoveAction : IRjsActionBase
{
public RjsRemoveAction(string element)
{
this.element = element;
}
public string element { get; set; }
#region IRjsActionBase Members
public string Render()
{
return string.Format("$(\"{0}\").remove();", element);
}
#endregion
}
private class RjsEffectAction : IRjsActionBase
{
public RjsEffectAction(string element, EffectTypes effect, KeyValuePair<string, string>[] options)
{
this.options = new Dictionary<string, string>();
if (options != null)
{
foreach (KeyValuePair<string, string> pair in options)
this.options.Add(pair.Key, pair.Value);
}
this.element = element;
this.effect = effect;
}
public string element { get; set; }
public EffectTypes effect { get; set; }
public Dictionary<string, string> options { get; set; }
#region IRjsActionBase Members
public string Render()
{
string ret = string.Format("new Effect.{0}(\"{1}\"", effect.ToString(), element);
ret += ", {";
foreach (string key in options.Keys)
{
ret += string.Format("{0}: {1}", key, options[key]);
}
ret += "});";
return ret;
}
#endregion
}
/// <summary>
/// Call me from your controller and pass me your ControllerContext and I'll render a UserControl for you and return the contents as a string!
/// </summary>
/// <param name="userControl"></param>
/// <param name="viewData"></param>
/// <param name="controllerContext"></param>
/// <returns></returns>
private string RenderPartialToString(string userControl, object viewData, ControllerContext controllerContext)
{
HtmlHelper h = new HtmlHelper(new ViewContext(controllerContext, new WebFormView("omg"), null, null), new ViewPage());
var blockRenderer = new BlockRenderer(controllerContext.HttpContext);
var r = new RjsResult();
JavaScriptSerializer serializer = new JavaScriptSerializer();
string s = blockRenderer.Capture(
() => RenderPartialExtensions.RenderPartial(h, userControl, viewData)
);
return serializer.Serialize(s);
}
#endregion
#region Fun Enums
public enum Positions
{
Before = 1,
After,
Top,
Bottom
}
public enum EffectTypes
{
Appear = 1,
Fade,
SlideDown,
SlideUp,
Shake,
Highlight
}
#endregion
#region Rjs Methods
/// <summary>
/// Renders a user control and inserts the contents into specified element
/// </summary>
/// <param name="element"></param>
/// <param name="p"></param>
/// <param name="userControl"></param>
/// <param name="viewData"></param>
/// <param name="controllerContext"></param>
public void Insert(string element, Positions p, string userControl, object viewData, ControllerContext controllerContext)
{
Insert(element, p, RenderPartialToString(userControl, viewData, controllerContext) );
}
/// <summary>
/// Inserts content into specific element
/// </summary>
/// <param name="element"></param>
/// <param name="p"></param>
/// <param name="contents"></param>
public void Insert(string element, Positions p, string contents)
{
Actions.Add(new RjsInsertAction(element, p, contents));
}
/// <summary>
/// Update a part of the page by rendering a user control
/// </summary>
/// <param name="element"></param>
/// <param name="userControl"></param>
/// <param name="viewData"></param>
/// <param name="controllerContext"></param>
public void Update(string element, string userControl, object viewData, ControllerContext controllerContext)
{
Update(element, RenderPartialToString(userControl, viewData, controllerContext));
}
public void Update(string element, string contents)
{
Actions.Add(new RjsUpdateAction(element, contents));
}
/// <summary>
/// Alert some text
/// </summary>
/// <param name="message"></param>
public void Alert(string message)
{
Actions.Add(new RjsAlertAction(message));
}
/// <summary>
/// Render some javascript code.. whatever you want
/// </summary>
/// <param name="contents"></param>
public void RenderString(string contents)
{
Actions.Add(new RjsStringAction(contents));
}
/// <summary>
/// Shows an element
/// </summary>
/// <param name="element"></param>
public void Show(string element)
{
Actions.Add(new RjsShowAction(element));
}
/// <summary>
/// Hides an element
/// </summary>
/// <param name="element"></param>
public void Hide(string element)
{
Actions.Add(new RjsHideAction(element));
}
/// <summary>
/// Delets an element
/// </summary>
/// <param name="element"></param>
public void Remove(string element)
{
Actions.Add(new RjsHideAction(element));
}
/// <summary>
/// Calls an effect of type EffectTypes, also takes array of prototype options
/// </summary>
/// <param name="element"></param>
/// <param name="effect"></param>
/// <param name="options"></param>
public void Effect(string element, EffectTypes effect, KeyValuePair<string, string>[] options)
{
Actions.Add(new RjsEffectAction(element, effect, options));
}
public void Effect(string element, EffectTypes effect)
{
Actions.Add(new RjsEffectAction(element, effect, null));
}
#endregion
#region ActionResult Override
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (this.SetContentType)
response.ContentType = "application/javascript";
string result = string.Empty;
foreach (IRjsActionBase action in Actions)
{
result += action.Render();
}
response.Write(result);
}
#endregion
}
}
One Major Gotcha
A trick to getting this to work with Prototype’s Ajax.Request is the ContentType of the returned data needs to be of type “text/javascript” in order for it to automatically be evaluated as javascript. However, you can get around this by forcing Prototype to evaluate the response data as javascript via the EvalJS: ‘force’ parameter.
So that’s it. A working way to RenderPartial to a string on the server-side and also the startings of a Rails-like RJS framework for ASP.NET MVC!
Thoughts, comments, patches or otherwise are appreciated!
Andy Brudtkuhl November 16th, 2008 at 11:16 pm
told ya asp.net mvc rocks!
Lorenz Cuno Klopfenstein January 4th, 2009 at 7:48 pm
Just what I was looking for. Works great, thanks!
Maico Tamayo January 5th, 2009 at 1:36 am
I also have this problem:
“Server cannot set content type after HTTP headers have been sent”..
I used a simmilar code like this:
” string s = blockRenderer.Capture(
() => RenderPartialExtensions.RenderPartial(h, userControl, viewData)
);”
I tried to get the string value of a control inside another control’s codebehind..
But I kinda change my ContentType to “text/ajax”.. Is there another solution for this rather than the solution written above? It will be a great help if there is one..
But all in all this was a great article, My world came crashing down when the RenderUserControl was scrapped and changed to RenderPartial that does not return a string value of the rendered control..
Kevin January 5th, 2009 at 6:54 pm
@Maico Thanks for the props. I wasn’t able to find a better solution to the problem you’re having with the ContentType. Seems to be a limitation and/or bug with the ASP.NET, according to a few articles I stumbled across.
Nik Coughlin January 11th, 2009 at 8:20 pm
This is totally excellent. I was having a problem with RenderPartial where if I had an override of Render anywhere in the MVC pipeline to do some work of my own, RenderPartial would output to the top of the outputted file instead of in the correct place, and I fixed it using this as a workaround. More details here on the asp.net mvc forum.
I also changed it to be an extension method on HtmlHelper so that I didn’t have to pass so many args in.
Thanks again!
Charles E. Walk Jr. January 20th, 2009 at 12:16 pm
I want to thank you for your solution. This is most definitely a problem I was having with a project I was on. The only problem I had was I wanted a simple solution that didn’t require a whole bunch of code. Now don’t get me wrong, this isn’t a whole bunch of code, but I wanted something more scaled down. While working in the ASP.NET world since it’s inception, I had issues where I wanted to perform this same process with AJAX and there really wasn’t an easy way to do it. Well, one day, I started researching control rendering and found some articles on google about the renderControl method. I set out to create an implementation using this functionality and produced a functional solution to my problem. So, I thought why not do the same thing with the ASP.NET MVC Framework. Through trial and error, I was able to get a working solution to this problem also. Here’s the code – http://developersisland.blogspot.com/2009/01/renderpartial-to-string-in-aspnet-mvc.html .
Now, I may be completely in left field, but I think this works far easier and I may be wrong or this is just another way of it. I am open to feedback. Take Care and Have a Great Day.
Dan Atkinson January 28th, 2009 at 3:06 am
For those who wish to look at alternates, I had some problems with this with redirecting, and found my solution at Stack Overflow:
http://stackoverflow.com/questions/483091/render-a-view-as-a-string
Jon Kruger February 20th, 2009 at 11:29 am
I had to change my RenderPartialToString() to this to get it to work:
/// Static Method to render string – put somewhere of your choosing
public static string RenderPartialToString(string userControlPath, object viewData, ControllerContext controllerContext, TempDataDictionary tempData)
{
HtmlHelper h = new HtmlHelper(new ViewContext(controllerContext, new WebFormView(userControlPath), new ViewDataDictionary(viewData), tempData), new ViewPage());
var blockRenderer = new BlockRenderer(controllerContext.HttpContext);
string s = blockRenderer.Capture(
() => RenderPartialExtensions.RenderPartial(h, userControlPath, viewData)
);
return s;
}
In my controller, I pass in this.TempData for that new 4th parameter.
Kyle Simpson April 10th, 2009 at 4:56 pm
I just implemented this workaround into our application, because apparently this lack of a string-writer argument being respected by ASP.NET core is still a problem even in the full release.
We needed the ability to have an Action that built up a rich json object, and included as one of its values was some html from a Partial.
I’ve got success now using this workaround. But I did run into the problem of the “can’t send headers” server error. It was because I couldn’t return my overall result as a JsonResult like I wanted (in other words, to let MVC automatically serialize my data object into a JSON string to return).
So, I changed my action to return ContentResult (which apparently doesn’t add any other response headers), and I built up my JSON object manually by first passing it to JavascriptSerializer.Serialize(), which packages up the JSON as a string, and then it’s fine to return to the browser as a ContentResult.
Hope this helps someone.
Martin April 26th, 2009 at 5:47 pm
Hello,
i think this approach is a lot more simple, and if the performance tests tells the truth also a lot faster.
http://thriftybliss.spaces.live.com/blog/cns!58DA805F37F31F20!170.entry
Thanks for sharing.
Best regards
Martin
Kevin April 27th, 2009 at 1:28 am
@Martin
I tried out your approach and my initial findings are really positive. That code is definitely much cleaner than the approach in this post. Plus, it doesn’t require any outside libraries. It also solves the ContenType problem listed earlier in this post – really nice!
I made a small change to your method so that it accepts a generic object for the viewdata, which gets past along to the ViewPage that gets created. This allows you to pass a custom viewdata object to the usercontrol (something we do a lot here).
public static string RenderPartialToString(string controlName, object viewData)
{
ViewDataDictionary vd = new ViewDataDictionary(viewData);
ViewPage vp = new ViewPage { ViewData = vd };
//(the rest of the method is unchanged)
}
// calling method somewhere in a controller...
ClientNotesRowViewData viewData = new ClientNotesRowViewData();
string s = RenderPartialToString("~/Views/Shared/Controls/Admin/Clients/Notes/ClientNotesRow.ascx", viewData);
Curious if anyone else has tried Martin’s approach and might see any flaws in it? It seems really solid.
Ozkan April 28th, 2009 at 8:47 am
Hi,
Thank you for your post
But I need to call this method from web service because I’m trying to get my viewusercontrol as a string from a javscript which calls a web service function. So controllername or control things aren’t work for me. is there a way for doing this way or am I trying in a wrong way??
PS. I’m a newbie on mvc, so please accept my apologies if my question is irrevelant
Maico Tamayo April 29th, 2009 at 6:03 am
To Ozkan,
Why would you make a webservice just for that, calling a controller method is way much faster than calling a webservice method.. Please put a fragment of your code for me to properly help you.
Ozkan Ozturk April 29th, 2009 at 10:25 am
To Maico,
I just figured the way to use a controller via javascript function out so I think you are right, there is no need to use it with web services. But now I have another problem. I exactly copy – paste the code provided here at top of this page. I’m getting null error on htmlhelper line. I’m using mvc 1.0, latest version mvc contrib. What should I do now??
Ozkan Ozturk April 29th, 2009 at 10:42 am
Hi, its me again.
In my previous post I didn’t write here what is wrong exactly, so I’m doing it now.
HtmlHelper h = new HtmlHelper( new ViewContext( controllerContext, new WebFormView( “msg” ), null, null ), new ViewPage() );
on this line I get two errors:
- Value cannot be null. viewdata
- Value cannot be null. tempdata
Now, I need your help
Ozkan Ozturk April 29th, 2009 at 10:54 am
I made it! It works like a charm
Now, how do I do it?
I passed tempdata and valuedata parameters to RenderPartialToString function because htmlhelper constructor needs these parameters.
I mean I call that function like this from my action:
RenderPartialToString( “/Views/try1/tempView.ascx”, ViewData, TempData, this.ControllerContext );
And I also made a change on RenderPartialToString function like:
public static string RenderPartialToString( string userControl, ViewDataDictionary viewData, TempDataDictionary tempdata, ControllerContext controllerContext )
{
HtmlHelper h = new HtmlHelper( new ViewContext( controllerContext, new WebFormView( “message” ), viewData, tempdata ), new ViewPage() );
var blockRenderer = new BlockRenderer( controllerContext.HttpContext );
string s = blockRenderer.Capture(
() => RenderPartialExtensions.RenderPartial( h, userControl, viewData )
);
return s;
}
I used contentresult for my action and that’s all, it works!
I think, the example has to be changed that way to work.
Anyway, thank you so much!
Have a nice day!
Martin April 29th, 2009 at 11:15 am
Use my method instead (look in the comments).
Just add the control (partial) to a viewpage and make it render the controls and read out the result.
Miha Markic April 30th, 2009 at 2:59 am
@Martin: I get viewContext null exception with your approach. I guess viewContext should be set, but you are not setting it.
Martin May 1st, 2009 at 4:48 am
@Mika Markic: You just have to add the ViewContext to your ViewPage. I guess you are probably using the HtmlHelpers which needs the ViewContext to be set.
Look at my post (in the comments) for an example of an extension method that probably does what you want.
Miha Markic May 1st, 2009 at 4:50 am
@Martin: Right, I’ve already added that and that part works. Unfortunatelly I’ve immediately got another showstopper – nested renderpartials won’t work (if you “rendered to string” control calls another renderpartial).
Maico Tamayo May 11th, 2009 at 10:10 am
To Ozkan,
Sorry for my late reply, I was so busy with my work, we also experienced that same problem when our company migrated from mvc beta to 1.0, and we found out to prevent this error, you must really but viewData or at least instantiate it properly for your RenderPartialString method, I don’t know your situation, but in our case it worked.. It seems working, maybe the MVC team is a little strict nowadays when it comes to rendering a page with a null viewdata,I really don’t know.But I hope this helps you.
Jon May 26th, 2009 at 9:42 am
Thank You,Thank You,Thank You! This is exactly what I needed. I am returning a JSONResult now with the output from my Partial!
Jon Kruger June 6th, 2009 at 8:59 pm
I think I found a way around the limitation where you can’t change the content type. It seems to be working for me so far.
http://jonkruger.com/blog/2009/06/06/aspnet-mvc-rendering-a-partial-view-to-a-string/
Martin June 17th, 2009 at 3:37 pm
Jon Kruger: My approach doenst have any problems with content-type.
Anyway, i just wanted to add that nesting the custom render partial works just fine (i use it for a email template service).
If you need to make the helpers work (html/ajax/url) remember to call InitHelpers() on the ViewPage (and add ViewContext / ControllerContext).
Lorenz Cuno Klopfenstein August 6th, 2009 at 2:47 pm
Hello people,
I found a new solution that doesn’t involve messing with the filters of the current HttpResponse and shouldn’t have problems with ContentType and other headers (it isn’t quite as elegant as Martin’s solution, but it also works for views that inherit from ViewPage instead of UserControls only).
Here it is on StackOverflow: http://stackoverflow.com/questions/483091/render-a-view-as-a-string/1241257#1241257
Hope it helps.
Greetings
Dan Atkinson August 7th, 2009 at 2:26 am
Lorenz,
If you look up at the comment on January 28th, you’ll see that I mentioned this.
Thanks,
Dan (the guy who originally created the SO question!).
Kevin October 12th, 2009 at 6:35 pm
Hello all, I’ve updated this post to link to a new article, which makes renderpartial to string substantially easier in ASP.NET MVC
Wessel August 20th, 2010 at 3:33 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
Render a view as a string — HTMLCoderHelper.com November 14th, 2010 at 4:40 am
[...] RenderPartial to String in ASP.NET MVC Beta If I use this example, I receive the "Cannot redirect after HTTP headers have been sent.". [...]
xinqikan November 17th, 2010 at 2:06 am
Is ASP.NET MVC’s FileStreamResult less efficient than writing directly to the Response Output Stream, or am I missing something?