Master Pages

Tuesday, July 19, 2005

My latest article: Master Pages In ASP.NET 2.0. Excerpt:

The master page implementation outlined above has another consequence to watch for. Since the master page injects it's controls and markup into a page’s Controls array, any relative URLs in the master page markup may break. Remember, the browser will request the web form, not the master page, so all of the URLs will be relative to the web form. When the web form and master page are in different directories, relative URLs may point to the wrong location. To help alleviate relative URL problems, ASP.NET will rebase relative URLs for server-side controls. Rebasing will build the correct URL to the resource.

P.S. Every tool ever made to format code into HTML has at least one problem that drives me insane.


Comments
Kevin Tuesday, August 2, 2005
If two different pages are using the same master page, how can you run different "body onload" code? Is there way to programatically change the body onload code for the master page depending on which content page you're on?
scott Tuesday, August 2, 2005
Yes, what I would do in that case is use a body tag like:

<body runat="server" id="body">

This will turn body into a server side control you can interact with - body will be an instance of an HtmlGenericControl on the server.

You could then add a method or property to the master page that will let you add the onload script. The call might look something like: Page.Master.SetBodyOnLoadScript(...);

And the SetBodyOnLoadScript could add the script using body.Attributes["onload"].

Make sense? This is a good question, I'll try to work up a working example and post it later in the week.
David Wednesday, August 24, 2005
My question is how you would reference a custom user control you place in a master page. So if you had a master page that contained a custom control named CustomUserControl.ascx with a prefix id of "navControl", then how would you get at it from a content page?

I know you would normally get controls in the master with a Master.Page.ControlName approach, casting to your target type (Label for example). But how do you work with a custom made control like CustomUserControl.ascx in the master?

I had seen some postings saying you would need to have your customer control implement an interface, but there has to (I sure hope) be a better way than that.

Thanks,

David
Scott Allen Wednesday, August 24, 2005
Offhand I can think of a couple ideas.

One involves an interface: odetocode.com/Blogs/scott/archive/2005/8/1.aspx

Also, you should be able to use an @ Reference directive:

odetocode.com/.../1889.aspx

Let me try to put up some sample code this weekend...
Dhiren Monday, August 29, 2005
I am trying to work with the master page and I have used <Asp:table> instead of html table and I am started having problem designing the Content page in the visual studio designer.

Basially, I am unable to work with content page associated with the master page in design view at all.

Please let me know if u have any idea ?

thanks,



Neil Monday, August 29, 2005
Question:

My asp.net app has several pages that implement client-side enabling/disabling of controls. The javascript is referenced by "include" statements in the header. If I use a master page, how can I get this javascript to be included?
scott Tuesday, August 30, 2005
Neil: I would look at using the Page.RegisterClientScriptBlock or Page.RegisterStartupScript methods during the Page_Load event of the master page.

Dhiren: I'll look at this during the week and get back, I don't have an answer off the top of my head, unfortunately.
djroelfsema Tuesday, August 30, 2005
Great article. Just when i needed it :-)
Peter Wednesday, August 31, 2005
Great article!
If only there was a way to get the css url()
to work when you develop on a xp mashine and deploy on a server as a root application.

on xp you have a vdir as root ( /myapp/ )
but the server is simply /

scott Friday, September 2, 2005
Peter: I think I've discovered that a great way to manage the css url is to put the style into a stylesheet in a theme. Relative URLs will work from there.

Dhiren: I've tried the <asp:Table> and have not had any problems. What error message do you see?
deodatus Sunday, October 2, 2005
"body onLoad" example.
Scott, I am trying to locate a working example with Page.Master.SetBodyOnLoadScript(...)you mentioned on August 01. Could you post a link to it?
Thanks
scott Tuesday, October 4, 2005
Deodatus: I didn't work up an example as yet ... I did find an article that covers this topic: www.devsource.com/article2/0,1895,1856091,00.asp
Allen Monday, October 17, 2005
Hi there as a newbie to .net 2005 I found your article very interesting. However, when I use the code you provide I get build errors that tell me Name 'Footer' is not declared. This is for Return Footer.Text and Footer.Text = vlaue in the Public Property FooterText()As String section of the Main.master.vb form. Any help would be greatly appreciated.
scott Tuesday, October 18, 2005
Hi Allen,

Do you have a <asp:Literal ID="Footer" runat="server" Text="foo" /> in your master page?
Allen Monday, October 24, 2005
<quote>
Hi Allen,

Do you have a <asp:Literal ID="Footer" runat="server" Text="foo" /> in your master page?
<unquote>

Yes, I copied it all as per your site.
scott Wednesday, October 26, 2005
Feel free to email me the code so I can take a look. There must be something one of us missed!
Paulo Aboim Pinto Wednesday, November 16, 2005
Hello ..
Thanks for the good article, but implementing my own solution I got a small problema that I want to ask you.

I'm developing a Enterprise Master Page and a Application Master Page.
One is generic to get the company default and the other is the implementation that what we want for Web Application and/or Module.

In the Application Master Page I want to insert a Search Form, and when we push the Search button, I want that default.aspx (or another) receive the POST and redirect for the right ASCX that know how to search in the Web Application.

How can I make my Master Page comunicate with the page that call?
I know how to do the oposite, creating in Master Page properties and in page we can access. Now I want to use other way: Post in Master Page and current Page get the value.


tkx in advance
Paulo Aboim Pinto
Odivelas - Portugal
Fayez Friday, November 18, 2005
Hi Scott,
I just started working with masterpages, and I do not have any control on positioning server controls on the contentpages, when I drag it from the tool box in design mode, it is inserted in the top left corner of the content page, and I cannot move it around.
How can I build a complex interactive web page within the contents page? is the only way to create multi-cell html table, and locate every server control in a cell?...
Sorry it could be trivial question for you, but I cannot use the designer that way..
Can I get some hint, Scott?
regards
scott Monday, November 21, 2005
Hi Fayez:

That's how HTML typically works, I believe the default in VS 2003 was a "grid" mode that was a bit different.

You can get exact layouts using tables and / or CSS.

Here is one article to get started with: msdn.microsoft.com/.../default.asp
Lerien Wednesday, November 30, 2005
have this within the <system.web> as shown below:

<pages masterPageFile="~/MasterTemplates/LayoutD.master">

I am certain that "~/MasterTemplates/LayoutD.master" exists as I have applied that master file on the Page directive of an individual aspx page.

Now on a new page, which I called "page6.aspx", that doesn't any "MasterPageFile" specified within its Page directive. When running the web app and browsing to page6.aspx, it doesn't render "~/MasterTemplates/LayoutD.master" as its master page.

Can you help please??

Thanks a bunch!!!!

Bob Wednesday, December 7, 2005
But isn't it true that something like this
<head runat="server">
<title>Untitled Page</title>
<link href="~/css/styles.css" rel="stylesheet" type="text/css" />
</head>
still is only working on pages based on this masterpage, the masterpage itself will not display correctly (loading the css) in the design view? At least it will not for me. That include images with ~/ as well if there made like
<img src="~/images/tree.png" runat="server" alt="5" />
but they do work when made like
<asp:Image ID="Image6" runat="server" ImageUrl="~/images/tree.png" AlternateText="6" />
Tyrone Wednesday, January 4, 2006
Hi Scott,

Great Article.

Just one question. I have 2 master pages that both have a property SubTitle. Now, I am choosing to load one of these master pages at runtime. What I can't seem to figure out is how can I set this SubTitle property when the page loads if I don't know, until runtime, which master page is being loaded.

Your example says to do:

Ctype(Master, otc).[PropertyName]

However, the otc master page my not be the one that is currently loaded. Any suggestions on how to get around this? Thanks
scott Thursday, January 5, 2006
Tyrone:

I'd have both master pages inherit from an interface (or another base class). The interface/base class can define the properties that all of your master pages must have.

Make sense?
Luke D Friday, January 13, 2006
Scott,

You mention that ASP.NET autmomatically rebases relative urls for user controls and master pages. Do you know if there is any way to have a control/master page where relative (not app relative) urls are NOT rebased?

I have a asp:Menu on a master page that has links that should be rendered as page relative, not absolute. So "index.aspx" should be app/execution_dir/index.aspx, not app/controls/index.aspx like it's being rebased to.

I can't find any information about this... I was thinking maybe a VirtualPathProvider that didn't do anything to relative urls... seems like overkill though. It might screw up my site elsewhere.

Luke
Luke D Friday, January 13, 2006
Got it,

In the control Page_Load event I do:

string dir = VirtualPathUtility.GetDirectory(VirtualPathUtility.ToAppRelative(Context.Request.RawUrl));
foreach (MenuItem item in Menu1.Items)
{
item.NavigateUrl = VirtualPathUtility.Combine(dir, item.NavigateUrl);
}

This isn't a configuration solution, but it solves my immediate problems. I still wonder if there is a way to disable rebasing on a control by control basis though.

Luke
Will Monday, February 27, 2006
Very nice article.

Thanks!
Luke Muszkiewicz Friday, April 7, 2006
Re. "And the SetBodyOnLoadScript could add the script using body.Attributes["onload"]."

.master:

<body id="htmlBody" runat="server">

.cs
((HtmlGenericControl)Page.Controls[0].FindControl("htmlBody")).Attributes["onLoad"] = "OnLoadJavaScript();";



Chris Wednesday, April 12, 2006
Scott,

When using master pages, can I have a page_load subroutine in the content page? I am having trouble getting information out of a cookie that is used in some sql calls when I put in a page_load in the content page. I may have a problem with putting the page_load event in the master page and correctly pulling the specific data out of the cookie to make certain queries work on different content pages.

I hope this makes sense.

Thanks
Chris
Rental Tool Box, Inc.
http://www.rentaltoolbox.com
scott Wednesday, April 12, 2006
Chris:

Should not be a problem, both the content page and the master page can have a Page_Load event handler (just remember the Page's Page_Load event handler fires first).
Chris Wednesday, April 12, 2006
Scott,

I have attempted to use a Page_Load subroutine to pull a value out of a cookie and place it in a Session variable for later use on the page when binding data to a datagrid. The only problem is the code does not appear to be running at all. I have a datagrid in the content of the page that performs a simple select...where and the where value is the cookie value from the Page_Load. The datagrid doesn't populate ever and to make matters worse for me, I can't even get a simple Response.Write to show me what is in the Session variable.

It is critical that I find a solution to this problem because without the ability to perform tasks in the Page_Load, I can't do things like check a cookie to allow access to specific pages or functionality in the application.

Any help is appreciated.

Here's the simple code in the Page_Load:

Dim UserCode As String

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If Not Request.Cookies("UCodeCookie") Is Nothing Then
UserCode = Server.HtmlEncode(Request.Cookies("UCodeCookie")("Code"))
Session("UserCode") = UserCode
End If
End Sub

I know the condition works because I can use the same condition in a button_click event and get the correct results.

Thanks

Chris
scott Thursday, April 13, 2006
If Page_Load isn't executing I'd suspect the method isn't wired to an event.

You either need: AutoEventWireup="true" in your @ Page directive, or ..

Handles Me.Load on the Page_Load method.
Chris Thursday, April 13, 2006
Scott,

The AutoEventWireup="false" was set by default in the Microsoft Visual Web Developer Express software for every content page that is created. Once I changed the value to AutoEventWireup="true", the Page_Load event executed. I guess I assumed everything was setup correctly by the software and never even thought of checking that value. I don't know if that value can be changed in the setup preferences for the software and I would hope that Microsoft changes this option to equal true in the next patch. I'm surprised no one else has had this problem or if they have, they are not posting to any of the common asp.net forums.

Thanks for your help.

Chris Benton
Rental Tool Box, Inc.
http://www.rentaltoolbox.com
Israel_Vencho Thursday, April 27, 2006
Hi Ia have this error "'Context' is not a member of 'default'" well what I want to do is use mi default.aspx.vb because I do not like use scripts in the default.aspx page I hope you can help me.
<%@ Page Language="VB" masterPageFile ="~/MasterPage.master" AutoEventWireup="false" CodeFile="default.aspx.vb" inherits="default" %>

<asp:Content ID="Content1" runat="server" ContentPlaceHolderID= "ContentPlaceHolder2" >
<div>
<asp:Button ID="Button1" runat="server" Text="Button" Width="135px" OnClick="Button1_Click" />
</div>
</asp:Content>
Sore_ron Friday, May 5, 2006
People found problems with the onload function in the masterpage running whenever acontentpage s run so I did a little digging..

The master page contains:

<%@ Master Language="VB" CodeFile="MasterPage.master.vb" Inherits="MasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "www.w3.org/.../xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>www.thevillalist.com</title>
<link href="StyleSheet1.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="index.js"></script>

'=======================================
The index.js page contains

function load()
{
var pagename = FileName() ;
var result = null;
switch (pagename) {
case "Default.aspx": subLoad(); break;
case "Default1.aspx": breadcrumbs(); break;
case "Default2.aspx": breadcrumbs(); break;
case "Default3.aspx": breadcrumbs(); break;;
case "Default4.aspx": breadcrumbs(); break;
case "Default5.aspx": breadcrumbs(); break;
case "Default6.aspx": breadcrumbs(); break;

default: Hello();
}

}
function Hello()
{
alert("hello");
}
'=======================================
Default3.aspc contains

<%@ Page Language="VB" MasterPageFile="~/MasterPage.master" AutoEventWireup="false" CodeFile="Default3.aspx.vb" Inherits="Default3" title="Florida Villas" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<script type="text/javascript" src="crumb.js"></script>

'=======================================
crumb.js contains

function breadcrumbs()
{
...
...
...
sore_ron Friday, May 5, 2006
Just to add ....

function FileName()
{
if (location.href.lastIndexOf('/') !=-1)
// Check whether '/' exists.
{
// If it does then we ...
firstpos=location.href.lastIndexOf('/')+1;

// Find the first position (the file starts after this)
lastpos=location.href.length;
/* Normally, the last position of the filename will be at the end of the complete URL - although it could have a # and a name at the end!*/
Namer=location.href.substring(firstpos,lastpos);
// We extract the string (the file's name).
// alert('My name is "'+Namer+'"');
// And reveal all to the clicker!
}
return Namer
}

Author of the Filename function

www.trans4mind.com/.../lastIndexOf.htm
Arkette Monday, May 22, 2006
Thank you for an excellent article on master pages. It is well written, easy to understand and addresses all of the oustanding issues I was having with there use.
ArdiGeek Monday, July 10, 2006
Hi,
I have found that the css path in the header works but the javscript links fail. For example:

<head runat="server">
<title>Test</title>

<link rel="stylesheet" type="text/css" href="Styles/Utilities.css" />
<script type="text/javascript" language="JavaScript" src="Scripts/menu.js"></script>
<script type="text/javascript" language="JavaScript" src="Scripts/tools.js"></script>

</head>

The css file works properly (even without the "~" root indicator but the javascript files do not load.
Bob C Wednesday, August 9, 2006
Scott,

I am using master pages at the root site (MOSS) and want pages on sub-sites to use the Images directory at the relative level, not the root level. unfortunately, the tag
<asp:Image runat="server" id="Image1" imageurl="../Images/AreaLogo.gif" />

Gets rendered as:
gr-spps-02:8443/_catalogs/Images/AreaLogo.gif

where _catalogs is the MOSS folder for the master page (ie _catalogs/masterpages/my.master) ! Any ideas?
Alex Saturday, August 12, 2006
Hi Scott,
I'm new to ASP.NET 2.0 and I would appreciate to know how far the capabilities of the Designer-Tool goes. When I'm switching to Absolute Position and I want to drag the ContentPlaceHolder to a appropriate Position on the MasterPage nothing happens. They are fixed ! Only controls like labels can be dragged around.
So I have to code the position by hand ?

Thx for information !

Alex
anuxp@yahoo.de
Scott Sunday, August 13, 2006
Alex:

Well, most people aren't using absolute positioning much these days, and I don't believe it really works with ContentPlaceHolder controls because they don't render anything. I'd suggest looking at some of the design templates for good advice.

msdn.microsoft.com/.../templates/
Nis L. Simonsen Wednesday, September 20, 2006
Nice article, one thing though:

You say that

"Since a content page cannot contain any markup outside of Content controls, there is no way for a content page to use a title tag, but there is now a Title attribute in the @ Page directive. There is also a Title property on the page itself.

For other elements that end up in the header, the Page class now exposes a Header property of type HtmlHead. You can use the HtmlHead reference to modify style sheet settings and add HtmlMeta objects as needed."

However this can easily be achieved by having multiple PlaceHolders, e.g.

Master page file:
<html>
<head>
<title>
<asp:ContentPlaceHolder id="TitlePlaceHolder" runat="server">No Title set</asp:ContentPlaceHolder>
</title>
</head>
<body>
<asp:ContentPlaceHolder id="Content1" runat="server" />
</body>
</html>

and then using two Content fields in the contentpage - one for the title element and one for the content.
Dominic Turner Wednesday, September 20, 2006
I have recently introduced a Master page to my site which i have been looking forward to for ages. It uses a CSS div layout.

The content pages that I am migrating use the RegisterStartupScript to register a block of Javascript that is used in the page. What I am finding is that the script is actually the last thing to be written to the rendered page.

As div's apparently mean that the browser can start drawing them before the whole page is loaded (despite buffer=true??????) I am getting errors now where the javascript functions are called before they are written to the page.

A typical example of this is where I have written a function to apply some kind of mouseover.

The errors are picked up in the IDE (which is nice) - and show that the page is not complete.

If I don't wazz around with the mouse and keep monk-like still until the page is loaded everything is hunky dory (because the script block is now there).

I never used to have this problem before master pages - is this because I am now using div's with their weird "loading before page is complete despite me thinking i am using a response buffer" behaviour or is something different happening with master pages about when exactly in the rendering of the page it decides to inject my javascript block??

I had the same problem with Microsoft's own controls javascript - the treeview control was freaking out about doing a mouseover before all the nodedata was loaded... eventually I made the div holding the navigation tree display:none and made it display:block once the page had loaded. This error happened all the time as your mouse was over the control when the new page was drawn.

I am thinking of using this technique now for the content div - however my problems would be solved in a less clunky way if I could get ASP.NET to emit my javascript block earlier in the page render.

Any ideas?
Hoc Tuesday, September 26, 2006
Thanks Scott for a great article. You write

"Unfortunately, there is no built-in capability to share master pages, and this article will only provide some advice."

Does this mean that it's impossible or merely really, really hard? ;)

We would like to share master pages by compiling them into a dll. Any pointers to where we would start? I've tried to find info on the subject but have not found anything. Implementing a class deriving MasterPage with it's own designer seems not to be enough, since Visual Studio seems to ignore our class specified in the MasterPage.master.cs code behind.

Any pointers or info will be greatly appreciated.
scott Tuesday, September 26, 2006
Hi Hoc:

IT's not impossible, just a little tricky (because there is no built in support). You also might want to take a look at WAPUL: odetocode.com/.../5469.aspx
Patrick Farrell Tuesday, October 31, 2006
I was looking for info on dynamically changing a Master Page's body tag's onLoad procedure from a content control.

Thanks for the hints. I wrote up a short description of how I did it in my at http://patf.net
Fiona Thursday, November 9, 2006
My page is 800px wide and I want to be able to center it within the browser and can't find how to do this. I am using master pages with div containers and css.

Any help most appreciated.
Thanks
Saurabh Friday, November 10, 2006
Hi Scott,

My compliments on your great work in sharing your knowledege.

I would like to know what extra features does the Master Page provides over and above a User Control?

Can we have a user control replicating the Master page functionality?

Thanks & Regards
scott Monday, November 13, 2006
Fiona:

Without seeing the markup I'm afraid I can't help much. The master page won't change the centering, you'll want to write the same HTML you always have to get the centered effect.

Saurabh:

A primary difference is that you can force all content pages to layout the same with a master page. User controls, on the other hand, are small re-usable pieces of a page.
Chris Monday, January 22, 2007
Scott,

I just wanted to say thank you for writing the superb article on Master Pages URL rebasing. I thought I was going insane and couldn't believe that .NET 2.0 Master Pages had any inherent weaknesses (it doesn't help when you're new to ASP.NET either !)

I have now adopted the runat="server" for the <img> tags on my Master Page so I can add mouse events to the images. Many thanks,

Chris
Maricris Reyes Wednesday, January 24, 2007
Hi, Scott, you're article about URL rebasing on master pages has been a great help for me, coz i'm having problem with some of my pages which is located in other directory separate from the master page. I just put the runat="server" on the <head> tag.

Thanks a lot!!!
gravatar Mary Friday, November 6, 2009
I'm pretty sure this is a stupid question. Can I use a master page created under vb.net with a C# content page?

I had some pages generated by a vendor and they use C#. Now I want to integrate these into our new website. If I didn't have to rewrite them, that would be great.

Thanks
gravatar scott Sunday, November 8, 2009
Mary:

In a web site project you can keep the master page and web forms in separate directories, and they can use different languages (C# and VB) and it all works. This doesn't work in a "web application" project, though.

Hope that helps.
henry Monday, April 12, 2010
thanks ,it helps me a lot
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!