Search code examples
asp.net-mvc-3master-pagesviewenginerenderaction

Setting masterName on MVC view causes error when calling RenderAction


I have a Masterpage (site.master) that calls a View using RenderAction. At the moment the View returns "hello world".

site.master:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<html>
<head id="Head1" runat="server">
   <title><asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server" /></title>
   <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server" />
</head>

<body>
    <% Html.RenderAction("Test", "Charts"); %>

    <asp:ContentPlaceHolder ID="ContentPlaceHolder3" runat="server" >
      <p>site.master</p>
    </asp:ContentPlaceHolder>

    <asp:ContentPlaceHolder ID="ContentPlaceHolder4" runat="server" />  
</body>
</html>

Test.aspx:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

hello world!

ChartsController.cs:

public ActionResult Test()
{
    return View();
}

If I update the View to pass in the name of the Masterpage explicitly I get an error when I call RenderAction.

ChartsController.cs:

public ActionResult Test()
{
    return View(null, "site");
}

Error:

Content controls have to be top-level controls in a content page or a nested master page that references a master page.

Stack Trace:

[HttpException (0x80004005): Content controls have to be top-level controls in a content page or a nested master page that references a master page.]
   System.Web.UI.MasterPage.CreateMaster(TemplateControl owner, HttpContext context, VirtualPath masterPageFile, IDictionary contentTemplateCollection) +8857854
   System.Web.UI.Page.get_Master() +54
   System.Web.UI.Page.ApplyMasterPage() +15
   System.Web.UI.Page.PerformPreInit() +45
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +328

How do I go about setting the master page that I want the view to use? Ultimately I will be setting the Masterpage dynamically using a custom ViewEngine (by overriding VirtualPathProviderViewEngine.FindView).

if ( String.IsNullOrEmpty(masterName) ){ masterName = "site"; }

When I set the masterName property in my ViewEngine and then call RenderAction from site.master I get the same error as when I set the masterName property in the Action.

I am using:
Visual Studio 2010
MVC 3
IIS Express

edited: added full site.master markup


Solution

  • I have come up with a solution and at least a partial answer/understanding to the cause of my issue.

    If I am correct the issue is that I as trying to set the Masterpage on a view that didn't have/need a Masterpage. I think that the result was that I was setting a path to a Masterpage and even though that Masterpage exists the View was not expecting it and through an error.

    When I updated the View to use a Masterpage I was able to pass the name of the Masterpage directly to the View without an error.

    Test.aspx:

    <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Charts.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
    
    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
        hello world!
    </asp:Content>
    

    The way that I resolved this in my custom ViewEngine was to check if the current View is a ChildAction.

    if (String.IsNullOrEmpty(masterName) && !controllerContext.IsChildAction)
    {
        masterName = "site";
    }