MvcContrib Grid Part 4 - Limitations of the WebFormsViewEngine

This is part 4 of a series about the new MvcContrib grid.

I was recently asked a question by one of the other MvcContrib comitters, Will Shaver, about why the ToString method of the Grid does not return the HTML for the grid, but instead writes the html its internal textwriter and then returns null.

This is the offending piece of code:

public override string ToString() 
{
	_gridModel.Renderer.Render(_gridModel, DataSource, _writer, context);
	return null;
}

This means that when ToString is called on the grid the GridRenderer will render the appropriate HTML to a textwriter (which is actually the HTTP Response output stream). From the end user's perspective, this shouldn't make a difference because it 'appears' that the grid is being rendered at the correct location:

<%= Html.Grid(Model.People).Columns(...) %>

ToString is implicitly called at the end of the <%= %> block which causes the rendering to occur.

What a silly way to create some html!

Yes, it may seem like a strange way of doing things but there is actually a good reason for this. In order to support rendering Partials for parts of the grid it is necessary that the grid is written directly to the response stream due to limitations of the WebForms View Engine.

What about passing a custom TextWriter to IView.Render?

The ASP.NET MVC IView represents the view that will be rendered. Take a look at the signature for its Render method:

void Render(ViewContext viewContext, TextWriter writer);

You might think that it would be possible to pass a StringWriter to the Render method, capture the output of the partial view and then store it internally in the grid. Unfortunately, this isn't possible. I'm guessing the TextWriter parameter here is a hook for a future extensibility point - the current implementation of WebFormView completely ignores the parameter. In fact, internally WebFormView has to make use of HttpContext.Current in order to work around the limitation of .aspx/web forms.

What about the BlockRenderer?

MvcContrib contains a class called the BlockRenderer that can be used to capture the outputs of a view by making use of a Response Filter. While I could have used this approach, it suffers from several major flaws. In order for this approach to work, it is necessary to Flush the response stream before inserting the CapturingResponseFilter. This causes problems if you're trying to do anything funky with HTTP Headers, or even something as simple as trying to set the Content Type. Check out this post on the MVC Forums for more details. There are other hacks that can be used, but none of them are ideal

In the end I decided the least evil approach was to render to Response.Output from within ToString as it causes the fewest problems for the end user. Hopefully with the release of ASP.NET 4.0 the MVC Team will be allowed to make some changes to the underlying WebForms engine in order to make it possible to capture view output.