ASP.Net MVC 3.0 Preview 1, Razor and nested master pages

Note: This blog posts is based on the ASP.NET MVC 3.0 Preview 1.

In this blog post I will explain how I have created a nested master page in ForkCan.com.

Here is the master of the master pages ;) (_Loyout.cshtml) (I have removed some HTML from this example only to reduce the code):


@inherits System.Web.Mvc.WebViewPage @using ForkCan.Web.Infrastructure.WebPageHelpers <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>@View.Title</title> @RenderSection( "TitleContent", optional : true) </head> <body> <div class="centerBlock"> ...
        <br class="clear" />

        <div id="header">
            <div id="topBar">
                <div id="topMenu">
                    <div class="nav">
                        <ul>
                            <li><a href="/">Code</a></li>
                            @if (WebSecurity.IsAuthenticated) {
                                <li><a href="/user/login">Log in</a></li>
                            } else {
                                <li><a href="/user/logout">Log out</a></li>
                            }
                           </ul>
                    </div>
                </div>

            </div>

            <div id="ch"/>

            ...

        </div>

        <div class="clear">

            <div id="mainmasterContent">

            @RenderBody()

            </div>

        </div>

        ...
 
        @Google.GetAnalyticHtml()

   
</body>
</html>

Something to point out in the _Layout.cshtml is the @RenderSection and @RenderBody mehtods. When using Master pages with Razor we can add the @RenderBody method where we want our “main content” of our content page to be located. If we want to specify some kind of “content place holders” we can use the @RenderSection methods. ForkCan.com has two “Content place holders” in the _Layout.cshtml, the RenderBody and the "RenderSection “TitleContent”. The TitleContent can be used from a “content page” to add styles, client-side scripts etc to the header of the _Layout.cshtml. To create a nested master page we just need to create a new “cshtml” file and then use the LayoutPage property to point to the master page we want our nested to be used (_TwoColumnsLayout.cshtml):

@inherits System.Web.Mvc.WebViewPage

@{
    LayoutPage = "~/Views/Shared/_Layout.cshtml";
}

<div id="right-content">

    @RenderSection("RightContent", optional : true)

</div>

<div id="content">

@RenderBody()

</div>

 

The content of the code above will be rendered where the @RenderBody of the _Layout.cshtml is placed. The nested masterpage in ForkCan.com have one “content place holder” added “RightContent” and the @RenderBody where the content of the content pages which will use the nested masterpage will be rendered. As you can see it’s quite easy to create nested master pages with Razor.

The following is one content page using the _TwoColumnsLayout.cshtml (It’s the view that renders the list of shared codes on Forkcan.com):

@inherits System.Web.Mvc.WebViewPage<ForkCan.Models.PresentationModel.ListOfShareCodes>
@using ForkCan.Web.Infrastructure.Extensions
@using ForkCan.Web.Infrastructure.WebPageHelpers

@{
    View.Title = "ForkCan - Top Used Code";
    LayoutPage = "~/Views/Shared/_twoColumnsLayout.cshtml";
}


@section RightContent
{
    <script src="/Scripts/jquery.tools.min.js" type="text/javascript"></script>

    <div class="rssFeed">
        <a href="@Model.FeedUrl"><img class="feedback" src="/content/rss.png" />@Model.FeedTitle</a>
    </div>

    <div>
        <span style="font-weight:bold;">Latest activity</span>
        @foreach (var activity in Model.LastActivities) {
        <div class="activityItem">
            @(new HtmlString(activity.ActivityText))
        </div>
        }
    </div>
}


    <div class="viewHeader">
        
        <h1>@Model.Header</h1>

        <div id="filters">
            <a class="@(Model.OrderBy == "views" ? "selectedFilter" : "")" href="@(Model.ListUrl + "?orderby=views&search=" + Server.UrlEncode(Helper.Request.Search))" title="Order by views">Credits</a>
            <a class="@(Model.OrderBy == "used" ? "selectedFilter" : "" )" href="@(Model.ListUrl + "?orderby=used&search=" + Server.UrlEncode(Helper.Request.Search))" title="Order by most used code">Used</a>
            <a class="@(Model.OrderBy == "newest" ? "selectedFilter" : "" )" href="@(Model.ListUrl + "?orderby=newest&search=" + Server.UrlEncode(Helper.Request.Search))" title="Order by newest code">Newest</a>
            <a class="@(Model.OrderBy == "oldest" ? "selectedFilter" : "" )" href="@(Model.ListUrl + "?orderby=oldest&search="+ Server.UrlEncode(Helper.Request.Search))" title="Order by oldest code">Oldest</a>
        </div>

   </div>
   
    <div id="container">
   
    @foreach (var code in Model.Items)
    {
        <div class='mySharedCodeItem'>

            <div id="shareCodeRating">
                <br />
                <span title="Number of views">@code.Views</span><br />
                Views
            </div>

            <div id="shareCodeItemLeft">

                <a href='/viewcode/@code.CodeId/@code.TitleInUrl' class="sharedCodeListItemTitle">@code.Title</a><br />

                <span class="posted">
                By <a href="/user/view/@code.CreatedById/@code.CreatedBy.ToUrlTitle()" class="sharedCodeListItemPostedBy" title='@(code.CreatedBy + " created this code")'>@code.CreatedBy</a>
                &nbsp;@code.CreatedByTitle&nbsp;
                @code.Created</span>

                <br />

               <br />
               <span class="codeDescription">&quot;@code.ShortDescription&quot;</span>

               @Html.Partial("_tags", code.Tags)

            </div>
            
            <div id="shareCodeItemRight">

                <div class="feedbacks">
                    <img class="feedback" title="Number of users used this code" src="/Content/used.png" /> @code.Used
                    <img class="feedback" title="Number of positive feedback" src="/Content/positive.png" /> @code.PosetiveFeedback
                    <img class="feedback" title="Number of negative feedback" src="/Content/negative.png" /> @code.NegativeFeedback
                    <img class="feedback" title="Number of discussions about the code" src="/Content/discussions.png" /> @code.Discussions
                </div>

            </div>

            <br style="clear:both;" />
         </div>

        <div class="sharedCodeItemEnd"></div>
    }

    @Pager.Render(Model.SelectedPage, 15, Model.TotalNumberOfCodeBlocks, Model.ListUrl + "?page={0}&orderby=" + Model.OrderBy +"&search=" + Server.UrlEncode(Helper.Request.Search));

    </div>

    <script type="text/javascript">
        $("a.tagLink").tooltip(
            {
                effect: 'toggle',
                position: 'bottom center'
            });
    </script>

 

The main content of the content page will be rendered where the @RenderBody method is located in the "_TwoColumnLayout.cshtml nested master page. To place something into the “content place holder” RightConent, we can in razor add a “content control” by using the @section syntax, the following code inside of the @section will be placed where the @RenderSection(“RightContent”) is located in the nested master page:

 

@section RightContent
{
    <script src="/Scripts/jquery.tools.min.js" type="text/javascript"></script>

    <div class="rssFeed">
        <a href="@Model.FeedUrl"><img class="feedback" src="/content/rss.png" />@Model.FeedTitle</a>
    </div>

    <div>
        <span style="font-weight:bold;">Latest activity</span>
        @foreach (var activity in Model.LastActivities) {
        <div class="activityItem">
            @(new HtmlString(activity.ActivityText))
        </div>
        }
    </div>
}

 

Summery

 

By using the LayoutPage property we can specify which master page we want to use. The @RenderBody should be used and places where the main content of a content page’s content should be rendered. By using the @RenderSection we can create “content place holders”, and fill them with content from a content page by using @section.

 

If you want to know when I publish a blog post to my blog, then you can follow me on twitter: @fredrikn

Published Sunday, August 1, 2010 10:16 AM by Fredrik N
Filed under: , , , ,

Comments

# re: ASP.Net MVC 3.0 Preview 1, Razor and nested master pages

Sunday, August 1, 2010 7:37 PM by cvertex

nice! this is what i've wanted to see and it's implemented perfectly. nested masters has been awesome since asp.net 2 or 3.5 or whatever

# re: ASP.Net MVC 3.0 Preview 1, Razor and nested master pages

Sunday, August 1, 2010 7:37 PM by cvertex

nice! this is what i've wanted to see and it's implemented perfectly. nested masters has been awesome since asp.net 2 or 3.5 or whatever

# re: ASP.Net MVC 3.0 Preview 1, Razor and nested master pages

Tuesday, August 3, 2010 10:56 AM by Jim Geurts

Does this allow you to specify a TitleContent in the content page?  Or can you just target placeholders in the immediate layout?

Leave a Comment

(required) 
(required) 
(optional)
(required)