Ok, I'm cruising through with ILSpy and trying to sort out what's going on here, but I'm not having much luck.
In a given view in ASP.NET MVC4 application (though, surely this is applicable to MVC3, possibly 2) that uses the Razor engine, the OutputStack
property inherited from WebPageBase
is simply a stack of TextWriter
objects.
By pushing and popping from this stack, one can manipulate the output of a given Razor view. I was hoping to leverage this with HTML helper extension methods, however I also noticed that ViewContext
, which is also a public member of HtmlHelper
has it's own TextWriter.
Now, I ran a quick check: (from within a view)
@object.ReferenceEquals(this.ViewContext.Writer, this.OutputStack.Peek()) // True
However, the following confuses me to all hell:
@{
// push one on
OutputStack.Push(new StringWriter());
}
Hello!
@{
// capture everything and pop it
string buffer1 = OutputStack.Peek().ToString();
OutputStack.Pop();
}
<pre>@(buffer1)</pre>
@{
// apparently it references the top of the OutputStack
// so this should be essentially the same
var oldWriter = ViewContext.Writer;
ViewContext.Writer = new StringWriter();
}
World!
@{
// revert it and hope for the best
string buffer2 = ViewContext.Writer.ToString();
ViewContext.Writer = oldWriter;
}
<pre>@(buffer2)</pre>
The results from the above are as follows:
Hello!
is captured into buffer1
, and actually dumped after as <pre>Hello!</pre>
. This is both expected and desired.World!
on the other hand, is output immediately, followed by an empty <pre></pre>
block; in other words, it's not capturing anything.My question is as follows: How do these TextWriter
objects relate to one another, and why can I not manage the reference through ViewContext
the same as I can from the top of the OutputStack
? (rather, how can I manage the reference through ViewContext
?)
Addendum
Details and other crap as I come across it.
Writer
property of ViewContext
doesn't discard values passed to the setter, so it's not just dropping it in the case of the second example.OutputStack
is actually coming from PageContext
.ViewContext.Writer
is set in WebViewPage.ExecutePageHierarchy()
using the WebViewPage
property Output
, which is the top of the OutputStack
! (is it just me, or is this beginning to look like a rat's nest of dependencies?)I am posting my comment as an answer. It might note solve the academic part of your question but it might definitely solve the practical part of the question and most importantly the initial problem you were trying to solve.
In your helper you could get the OutputStack
of the current view:
public static void MyHelper(this HtmlHelper html)
{
var stack = ((WebPageBase)html.ViewDataContainer).OutputStack;
... you could push and pop here and solve your real world problem
}