Main Menu
Home
News
Links
Contact Us
Search
Steve's Blog
Erica's Blog
Pictures
FTP Site
Downloads
Guitar Music
Wiki
SharePoint
Who's Online


Using your SharePoint site Master Page on all application pages PDF Print E-mail
Written by Steve Lineberry   
Saturday, 25 August 2007
Article Index
Using your SharePoint site Master Page on all application pages
Page 2

So I found this article, http://weblogs.asp.net/soever/archive/2006/11/14/SharePoint-2007_3A00_-using-the-masterpage-from-your-site-in-custom-_5F00_layouts-pages.aspx, which explains how to use your SharePoint site's master page when you create a custom application page in the _layouts virtual directory. At this point in time I don't really have any custom application pages yet but I liked the idea of when I theme a site that all pages, including application pages in the _layouts virtual directory, use the same master page. By default all application pages use the application.master master page. After finding this article, http://www.odetocode.com/Articles/450.aspx, and a lot of playing around I believe I have found a solution. I created a HttpModule that on a page's preinit, it checks to see if the page is in the _layouts virtual directory, and if so, it will assign it the master page url from the current sharepoint site. Here's my code:

public class ApplicationMasterPage : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
}

void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
Page page = HttpContext.Current.CurrentHandler as Page;
if (page != null)
{
page.PreInit += new EventHandler(page_PreInit);
}
}

void page_PreInit(object sender, EventArgs e)
{
Page page = sender as Page;
if (page == null) return;
if (page.MasterPageFile == null) return;
HttpContext context = HttpContext.Current;
string currUrl = context.Request.Url.ToString();
string basepath = currUrl.Substring(0, currUrl.IndexOf(context.Request.Url.Host) + context.Request.Url.Host.Length);
//Use RawUrl, otherwise it will always use the root web.
currUrl = context.Request.RawUrl;
if (currUrl.ToLower().Contains("/_layouts/") &&
page.MasterPageFile.ToLower().EndsWith("application.master"))
{
currUrl = currUrl.Substring(0, currUrl.ToLower().IndexOf("/_layouts/"));
SPSite site = new SPSite(basepath + currUrl);
SPWeb web = site.OpenWeb(currUrl);

page.MasterPageFile = web.MasterUrl;

web.Dispose();
site.Dispose();
}
}

public void Dispose()
{
}

}

Be sure to register the HttpModule in the web.config of your sharepoint site.

This did require me to add extra contentplaceholders into my default.master. Here are the two extra placeholders I had to add:

PlaceHolderPageDescriptionRowAttr
PlaceHolderPageDescriptionRowAttr2

To add these placeholders into my default.master I replaced the following code:

<PlaceHolder id="MSO_ContentDiv" runat="server">
<table id="MSO_ContentTable" width=100% height="100%" border="0" cellspacing="0" cellpadding="0" class="ms-propertysheet">
<tr>
<td class='ms-bodyareaframe' valign="top" height="100%">
<A name="mainContent"></A>
<asp:ContentPlaceHolder id="PlaceHolderPageDescription" runat="server"/>
<asp:ContentPlaceHolder id="PlaceHolderMain" runat="server">
</asp:ContentPlaceHolder>
</td>
</tr>
</table>
</PlaceHolder>

with

<PlaceHolder id="MSO_ContentDiv" runat="server">
<table id="MSO_ContentTable" width=100% height="100%" border="0" cellspacing="0" cellpadding="0" class="ms-propertysheet">
<TR valign="top" <asp:ContentPlaceHolder id="PlaceHolderPageDescriptionRowAttr" runat="server"/> >
<TD class="ms-descriptiontext" width="100%">
<asp:ContentPlaceHolder id="PlaceHolderPageDescription" runat="server"/>
</TD>
</TR>
<TR <asp:ContentPlaceHolder id="PlaceHolderPageDescriptionRowAttr2" runat="server"/>>
<TD ID=onetidMainBodyPadding height="8px"><IMG SRC="/_layouts/images/blank.gif" width=1 height=8 alt=""></TD>
</TR>
<tr>
<td class='ms-bodyareaframe' valign="top" height="100%">
<A name="mainContent"></A>
<asp:ContentPlaceHolder id="PlaceHolderMain" runat="server">
</asp:ContentPlaceHolder>
</td>
</tr>
</table>
</PlaceHolder>

I also had to change my default breadcrumbs code in the default.master to match the application.master. I replaced the following code:

<asp:ContentPlaceHolder id="PlaceHolderTitleBreadcrumb" runat="server">
<asp:SiteMapPath SiteMapProvider="SPContentMapProvider" id="ContentMap" SkipLinkText="" NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/>  
</asp:ContentPlaceHolder>

with

<asp:ContentPlaceHolder id="PlaceHolderTitleBreadcrumb" runat="server">
<asp:SiteMapPath SiteMapProvider="SPXmlContentMapProvider" id="ContentMap" SkipLinkText="" NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/>  
</asp:ContentPlaceHolder>

--------------------------------------------------------------------------------------------------

Update (02/23/08):  I've changed my default.master and my httpmodule around a lot since posting this.  If you don't compile a dll you will need to place the ApplicationMasterPage.cs file in a App_Code directory of your root site and in a App_Code directory at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\IMAGES. You can download my latest files below:

default.master

ApplicationMasterPage.cs

--------------------------------------------------------------------------------------------------

All of the code and changes ended up with a site settings page that looks like this.

Comments
Written by Paul on 2007-10-22 09:20:16
Hi there, 
 
for your HttpModule, what did you have to add to the web.config file? 
 
Also I take it that your HttpModule gets called for each page that is served, it's just that you chose to ignore it if it's not a page? 
 
I need to do some basic usage logging of when people download documents, I've created the module but it falls down when trying to register the dll in the web.config file. 
 
Thanks!
Written by steve on 2007-10-24 09:41:56
Check out the httpModules section of the web.config file for some examples of how to register your httpmodule. 
 
Yes, it gets called for each page that is servered, and I ignore it if it's not a page, or it doesn't have a masterpage, or if it doesn't exist in the _layouts virtual directory.
Written by steve on 2007-12-07 11:14:35
If you are getting the following error when trying to perform certain functions like checkin, workflows, and deleting site columns: 
Code:
 
Unable to validate data. at System.Web.Configuration.MachineKeySection.GetDecodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Int32& dataLength)  
 
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)  

 
This is caused by having the search box on these pages. I've started using a code behind page for my master page so I can hide one of the breadcrumbs and so now I can hide the search box when on a /_layouts/ page. My code has been updated above, check it out and let me know if it fixes your problem. Thanks.
Written by Harry on 2007-12-13 17:20:41
I would like to do the following in wss v3. 
 
Create my own site definition and then create my own master page which only has siteactions control and some Hello Text and a simple asp.net content place holder control and nothing else (I have read about minimal master page that applies to sts but I would like to build my own master and really would like to throw out out of the box sts templates). I would like to put this in _catalogs/master pages/ . Then I would like to have a layout page called Helloworld.aspx (which has simple html place holder control) that uses Helloworld.master. Then I would like to build a feature that lets me create Helloworld page from site actions menu. First, is this possible or I am always stuck with the minimal master page and sts site definition? Why is one site definition related to one master page? It would have been nice if I could select the master page I would like to use when creating a new page as opposed to creating a feature (May be that’s what I have to do to get this feature??). 
 
What I am really trying to do is understand inner workings of wss v3 better and it seems most of the books in the market talk about sts. It would be nice if there are articles that show how to build a simple helloworld.master and helloworld.aspx and a simple site definition (bare minimum). Do you know of any resources that might help me?  
Written by steve on 2007-12-14 07:46:23
Since you are talking about page layouts, I assume you have MOSS and not just WSS. What you are saying is possible i believe. From my experience, creating my own site definition wasn't an easy task. Everything else seems pretty doable.  
 
As far as relating a site definition to a master page, typically you can use one of the master page shortcut urls to always use the default.master of the current site. Or you can type in the static url to a specific master page. 
 
As far as resources go, check out the MOSS 2007 SDK. It comes with a tool that will convert a site to a site definition. In my experience it doesn't work that great but can get you going in the right direction.
Written by steve on 2008-02-23 20:05:19
I've posted some new HttpModule code that doesn't require the master page to have a code behind page. I also have this code setup on my personal sharepoint site: http://sharepoint.thelineberrys.com. So go there and click view all site content for an example.
Written by Jason on 2008-07-24 14:24:39
The default.master file has a typo in it that causing rendering issues on browsers other than IE. The following is incorrect: 
Code:
 
 

 
This is actually an ASP.NET PlaceHolder control. It needs to be changed to: 
Code:
 
 
... 
 

 
 
Changing this fixes some issues with borders being drawn incorrectly and not connecting to the bottom of the page.
Written by Summer on 2008-10-03 13:16:32
For some reason it wasn't replacing the application.master for me, so i decided to debug within visual studio attaching it to the w3wp procces, when it gets to: HttpContext.Current.CurrentHandler it is null, and thus doesn't ever replace it. 
Do you have any idea what would cause HttpContext.Current.CurrentHandler to be null? :? :?  
If anyone has input please respond! thanks much!
Written by Jm on 2008-10-29 12:43:02
Great & helpfull article. Thank you.  
 
Maybe you should add something like this below the construction of the variable basepath 
 
// check for port number 
if (context.Request.Url.Port != 80) 

string strPortExtension = ":" + context.Request.Url.Port.ToString(); 
basepath += strPortExtension; 

 
Otherwise you got the typical SharePoint Error Message if you're using ports other than 80. 
 
Greetings, 
 
Jm
Written by Steve on 2008-10-29 16:09:42
Jm, good call. One should also check for https. Thanks.
Written by This e-mail address is being protected from spam bots, you need JavaScript enabled to view it on 2009-02-12 09:27:01
Good article but when i modified your master page, i have a problem a page with "unknow error".. 
So i don't modified my master page with my template...



Last Updated ( Saturday, 23 February 2008 )
 
< Prev   Next >
Latest Blogs
   Home arrow Steve's Blog arrow Using your SharePoint site Master Page on all application pages