I'm trying to create a custom Razor view base class (inheriting WebViewPage
) that will inject a bit of HTML for each view template being rendered (including Layouts and Partial Views) so that I have a reference on the client of where each Razor template starts (not interested in where it ends).
What I have tried so far is
- overriding the Write method (as described in a comment here) . This injects code at every razor section, not just once per template (for example every time that you use the HTML.TextBoxFor)
- overriding the ExecutePageHierarchy method (as described in the post of the link above). This throws an error every time it hits the first
PopContext
call: The "RenderBody" method has not been called for layout page "~/Views/Shared/_Layout.cshtml".
after trying your solution, I had some problems with the rendered HTML of complex pages with partial views.
my issue was that everything was reversed. (order of partial views)
to correct - I ended up replacing the Output stream in the OutputStack
public override void ExecutePageHierarchy()
{
// Replace output stream with a fake local stream
StringWriter fakeOutput = new StringWriter();
// Save output stack top level stream, and replace with fake local stream
TextWriter outputStackTopOutput = OutputStack.Pop();
OutputStack.Push(fakeOutput);
// Run Razor view engine
base.ExecutePageHierarchy();
string content = fakeOutput.ToString();
// Set back real outputs, and write to the real output
OutputStack.Pop();
OutputStack.Push(outputStackTopOutput);
outputStackTopOutput.Write(content);
}
Think that I have an answer to this now:
public abstract class CustomWebViewPage: WebViewPage
{
public override void ExecutePageHierarchy()
{
var layoutReferenceMarkup = @"<script type=""text/html"" data-layout-id=""" + TemplateInfo.VirtualPath + @"""></script>";
base.ExecutePageHierarchy();
string output = Output.ToString();
//if the body tag is present the script tag should be injected into it, otherwise simply append
if (output.Contains("</body>"))
{
Response.Clear();
Response.Write(output.Replace("</body>", layoutReferenceMarkup+"</body>"));
Response.End();
}
else
{
Output.Write(layoutReferenceMarkup);
}
}
}
public abstract class CustomWebViewPage<TModel>: CustomWebViewPage
{
}
Seems to work, but if anyone has a better solution, please share.