Developers love hierarchies, inheritance and reuse. In ASP.NET it is possible to create a hierarchy of master pages through nesting, where child master pages inherit the HTML framework from its parent master page. As SharePoint is built on ASP.NET, one could assume that this feature is also available in SharePoint. But in fact, nested master pages are not supported at all, according to Microsoft documentation.
But there is hope. It is possible to achieve nested master pages throughout a SharePoint farm with only a couple of tradeoffs.
In ASP.NET, a ContentPlaceHolder defined in the parent master will be inherited to the child master. In SharePoint, it is not inherited, which is the main problem. To circumvent this, one needs to repeat the ContentPlaceHolder elements in the child master, so that they are accessible from content pages. The technique is also described in Ari Bakker’s blog post, Using Nested Master Pages in SharePoint. His blog post is written for SharePoint 2007, but the technique is still the same for SharePoint 2010.
<%@ Master Language="C#" %> ... <asp:ContentPlaceHolder ID="PlaceHolderMain" />
<%@ Master Language="C#" MasterPageFile="~sitecollection/_catalogs/masterpage/contoso_base.master" %> ... <asp:Content runat="server" ContentPlaceHolderID="PlaceHolderMain"> <asp:ContentPlaceHolder ID="PlaceHolderMain" runat="server" /> </asp:Content>
<%@ Page Language="C#" Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage, Microsoft.SharePoint.Publishing,Version=184.108.40.206,Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <!-- In Site Settings > Look and feel > Master Page, set the master page to contoso_publishing.master, and the system master to contoso_system.master --> ... <asp:Content runat="server" ContentPlaceHolderID="PlaceHolderMain"> <!-- Main content goes here. This will fill “PlaceHolderMain” in the CHILD master page, which in turn fills “PlaceHolderMain” in the PARENT master --> </asp:Content>
This works great, but there is still a bit of copy/paste work to be done. The content in the master placeholder is not inherited to the child placeholder, so you need to copy the content to the child master and the page layout, respectively. If you want to include the original content, that is.
A typical setup
An intranet is a good example of a solution that uses most of SharePoint's front-end features, as an intranet typically consists of the following elements:
All custom master pages inherit from contoso_base.master, except contoso_mysitehost.master. Why? Keep reading to find out.
There are a few gotchas that you will find rather annoying if you're not prepared. Luckily, you can read about them here.
Exception in MySite Host site
If you try to use an inherited master page for the MySite Host, you will encounter the following exception.
"Object not set to an instance of an object", with a stack trace that leads you to Microsoft.SharePoint.Portal.WebControls.MySitePublicWebPartPage.OnInit()
The ASPX pages in SPSMSITEHOST all inherit from this class, and using Reflector, you will notice that line 3 in OnInit looks like this:
ContentPlaceHolder holder = (ContentPlaceHolder)base.Master.FindControl("PlaceHolderLeftNavBar")
Now, if we were to create inherited_mysitehost.master, there would be no ContentPlaceHolder control named PlaceHolderLeftNavBar, only a Content control with its ContentPlaceHolderID set to PlaceHolderLeftNavBar. If we could only change the code to base.Master.Master.FindControl... But we can’t, obviously. This is why contoso_mysitehost.master needs to be an independent master page.
Seems like Frode Sørhøy also had the same problem, by the way.
MySite global navigation
In the world of MySite, the DelegateControl GlobalNavigation is used to provide navigation between My Newsfeed, My Content and My Profile. This usually collides with the graphic design you are implementing, and you should work with the designer to place this control further down the page where it might fit better.
Misplaced search box in search result pages
The Search Centre uses minimal.master, a master page that is rather stripped-down. Now, for some reason, the ContentPlaceHolder PlaceHolderTitleBreadcrumb in minimal.master is located just before PlaceHolderMain. Normally, PlaceHolderTitleBreadcrumb is placed in the upper left corner, and is usually populated by the breadcrumb button. If you create your own contoso_search.master that inherits from contoso_base.master, the PlaceHolderTitleBreadcrumb would still be placed in the upper left corner, and the search box appears in the breadcrumb flyout.
The solution to this is to mimic minimal.master, and put PlaceHolderTitleBreadcrumb inside PlaceHolderMain.
<asp:Content runat="server" ContentPlaceHolderID="PlaceHolderMain"> <asp:ContentPlaceHolder runat="server" ContentPlaceHolderID="PlaceHolderTitleBreadcrumb" /> <div class="s4-pr"> <asp:ContentPlaceHolder runat="server" /> </div> </asp:Content>
There is an alternative solution to this at SharePointRoot.
Also, check out Nick Hadlee's solution to this problem. Very neat if you don't intend to customize the master page.
It takes a bit of time to set up this master page framework the first time you do it, and it has a few pitfalls that will be a reliable source of frustration if you are not aware of them. But when everything is in place, you will have a consistent look and feel across your entire solution, and you only need to maintain your global HTML in one (OK, two) places. You also have some really useful experience for your next SharePoint project.