Master Pages are a wonderful concept that as developers we highly value. It’s the sort of pattern that just looks like the right thing to do (to our twisted, concept hungry developer minds) and that even makes you wonder why we haven’t done it that way since the beginning of time (1990). For the record, master pages were invented by David Ebbo, who is behind a lot of the smartest things in ASP.NET.
Just in case you have no idea, what are Master Pages? Before master pages, sharing layout between pages was done using includes (or user controls). For example, your typical hello world page might have looked like this:
<%@ Page Language="C#" %> <%@ Register TagPrefix="include" TagName="header"
Src="~/HelloHeader.ascx" %> <%@ Register TagPrefix="include" TagName="footer"
Src="~/HelloFooter.ascx" %> <!DOCTYPE html> <html> <head> <title>Hello world with includes</title> </head> <body> <div> <include:header runat="server" />
Hello world! <include:footer runat="server" /> </div>
Notice that the page does contain markup that is not specific to the page (boilerplate markup mainly that you could put into includes as well, but only at the cost of breaking beginnings and ends of semantic blocks into separate includes).
As a side note, I’ve always thought the user control syntax above was overly verbose when all you want is a plain include. It has the one advantage of enabling the Visual Studio designer but having a separate registration and declaration seems overkill. The #include directive still exists but I personally prefer to use a simple helper extension method to Page that brings the code down to this (MVC gives you Html.RenderPartial, which is roughly equivalent):
<%@ Page Language="C#" %> <!DOCTYPE html> <html> <head> <title>Hello world with includes</title>
<meta name="keywords" content="hello world" /> </head> <body> <div>
<% this.Include("HelloHeader.ascx"); %> Hello world!
<% this.Include("HelloFooter.ascx"); %> </div>
With master pages, you can get rid of all common markup and really limit what you put into the page to just what varies from page to page. for example, the above hello world page might be written like this:
<%@ Page Language="C#" MasterPageFile="~/MasterPage.master"
Title="Hello world!" %> <asp:Content ContentPlaceHolderID="head" Runat="Server"> <meta name="keywords" content="hello world" /> </asp:Content> <asp:Content ContentPlaceHolderID="content" Runat="Server"> Hello from the content page! </asp:Content>
That is clean, but pretty puzzling the first time you see it. To understand where the markup comes from, you need to track that @Page directive and understand that you need to look into the corresponding master page file:
<%@ Master Language="C#" %> <!DOCTYPE html> <html> <head runat="server"> <title>Master page</title> <asp:ContentPlaceHolder id="head" runat="server"> </asp:ContentPlaceHolder> </head> <body> <h1>Hello from a master page</h1> <div> <asp:ContentPlaceHolder id="content" runat="server"> </asp:ContentPlaceHolder> </div> Footer says hi. </body> </html>
You need to understand that those content place holder ids correspond to other controls that are in the master page and that the framework will make the match.
Master pages also come with the price of mangled ids, but that’s an implementation detail (one could imagine an implementation that wouldn’t suffer from that problem). The price we pay for master pages also comes in the form of a weird control tree: master pages are really implemented as user controls that get included by the page, a concept that is the inverse of the model they seem to promote; in other words, the implementation is the reverse of the concept, in a way. There are also complications about putting contents in the head section (head must be runat=server, script tags are tricky, setting the title, etc.).
So are master pages worth the price? It depends on who your audience is and on how your pages are built.
By audience, I mean the person who is going to write the views. If it’s just you and you are a developer, they might actually be a pretty solid choice. But if external designers are going to build the views, maybe you need to pause and try not to think as a developer for a moment.
The main problem with the include approach (and the reason why master pages were invented) is that the outer markup for the page needs to be on all pages. So if you decide to change that markup, you need to do so on all pages.
But in more and more applications, in particular CMS, this problem becomes moot. If your application decouples the content from the view code, and if the layout or view to use for a given content can be determined at runtime by a themeing engine, you might end-up with templates that look like this:
<%@ Page Language="C#" %> <!DOCTYPE html> <html> <head> <title><%= Title %></title> </head> <body> <div> <% this.RenderZone("Header"); %> <% this.RenderZone("Contents"); %> <% this.RenderZone("Footer"); %> </div> </body> </html>
This is actually no different in concept from master pages (there are place holders in generic markup and no specific contents) but the contents do not come from a content page. Instead, they are dynamically inserted by the application.
The template file then becomes no more than layout. It is easy to understand and easier to assign a different layout to any given contents.
So in general there is no perfect answer on whether you should use master pages or not, but if you are able, in your application, to decouple page layout from contents, there is an opportunity to have clean and easy to understand markup that also maximizes re-use.