Consistent look and feel in a SharePoint 2010 farm

Christian Nesmark

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.

The basics

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.

Parent Master

<%@ Master Language="C#" %>
...
<asp:ContentPlaceHolder ID="PlaceHolderMain" />

 

Child Master

<%@ 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 layout

<%@ Page Language="C#" Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage, Microsoft.SharePoint.Publishing,Version=14.0.0.0,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:

Original Custom Inherits
Base master v4.master contoso_base.master None
Publishing site nightandday.master contoso_publishing.master contoso_base.master
Collaboration sites v4.master contoso_collaboration.master contoso_base.master
My profile mysite.master contoso_mysitehost.master None
My content mysite.master contoso_mysite.master contoso_base.master
Search minimal.master contoso_search.master contoso_base.master
System v4.master contoso_system.master contoso_base.master

All custom master pages inherit from contoso_base.master, except contoso_mysitehost.master. Why? Keep reading to find out.

The pitfalls

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.

Search box in breadcrumb box

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.

Conclusion

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.