MasterPages in .NET v1.* -- Dynamic Masters

It seems that lately my implementation of MasterPages for .NET v1.* is getting a lot of hits again, which is kind of funny considering its been out for over a year.  Anyhow, one of the questions that I get asked a lot is how to dynamically change the master template at runtime, which is obviously possible since I do it on my own site.  The solution to this problem all comes down to timing -- the Init event is already too late, so you need something earlier, like the AddParsedSubObject override.  Just be aware that this method is called for every root object added to the page, so make sure that you wrap any code you add to this method so that it only gets ran once.  So here's the code:

protected override void AddParsedSubObject(object obj) {
  base.AddParsedSubObject(obj);
  if (obj is MasterPage) {
    ((MasterPage) obj).TemplateFile = "~/Template.ascx";
  }
}

Note that while you can add this code to each page, MasterPages doesn't preclude you from using a base page class -- I put this code in my base page class on my site.  Also note that you can use any of the Request collections (Form, QueryString, Cookies), or Session, as the basis of your choice for the TemplateFile to make it truly dynamic.  Finally, if you are already playing with .NET v2.0, then see my Whidbey article for a version of this code that uses the TestDeviceFilter override, although this may change as Whidbey matures.  Note that my Whidbey article also has a fully functioning version of everything in the article for the .NET v1.* scenario in the download -- so its not just about Whidbey.

13 Comments

  • Ha ha :) No doubt some of it is Scott -- and thank you very much ! Of course, some may also be due to the now public nature of the Whidbey previews.

  • Generally a great tool, but I'm still finding it very hard to programmatically set my header titles at runtime from a code behind class, whilst everything else seems to work as advertised.



    If I try to use a Literal I find that it's .Text property is

    always empty - I assume that I'm sending it the title string too late(?)



    Are there any docs lying about that give a good example of this?

  • I can't say what you're doing wrong without seeing your code, but almost everyone one of my many examples show techniques to get your titles working, and there are multiple ways.

  • Paul,



    I think your new and improved MasterPages implementation is fantastic, but I still have some reservations about performance. I've compared the new MasterPages approach with your page templating examples (using both direct render and user controls) and found that there general "slugish" feel to how pages load when using MasterPages. Looking at the trace output, the MasterPages approach is about 2-2.5 times slower than your other templating techniques. The aspx pages just don't have the same snappy feel with MasterPages. I was just wondering what your thoughts were on that?



    Thanks again for your work on this.

  • Hi James:



    There's no doubt it is "slower", although everything is relative to some degree. The direct render approach is definitely the "fastest" possible, since it totally avoids the performance "hit" of server controls, but it also offers the least design experience. The user control technique is a little faster than master pages since it doesn't have to rearrange the control tree, but it still lacks some flexibility that master pages provides. So the question really comes down to what is the performance "hit"? If you only compare the simplest of pages with the simplest of templates then the user control approach is about 30% slower and I think I recall master pages was about 60% slower. But that's not the whole picture at all -- there is typically much more going on then the simplest cases. I've found through experience that the performance "hit" on real-world pages, especially when there are things like database connections, is actually pretty neglible. Also, as a final comparison, even the simplest 60% slower master page is still faster than the equivalent classic ASP page since ASP.NET is compiled! So yes, master pages are "slower", but I haven't found it to be a concern when you look at the bigger picture -- and I believe my website proves it since it runs on master pages. In the end though, you have to do the tests and decide for yourself.



    Thanks, Paul Wilson

  • Not sure I understand this. where is the logic to decide which template.ascx is used?



    for example, if I have a button on the DefaulTemplate.ascx which i want to click to switch templates of _any_ page (e.g. to view the same page using PrintableTemplate.ascx), am i supposed to make this switch somewhere between the buttonclick and the Init event?



    so where do i override the AddParsedSubObject method? in the DefaulTemplate.ascx codebehind?



    or on each page?

  • You override the AddParsedSubObject method of the page, or a common base page to do it for an entire site. As to the logic, that's up to you, as everyone's will be different, but you do need to realize that you cannot do it in an event. Why? Because they all occur too late in the page lifecycle, and you need to do this even before the Init method/event in the AddParsedSubObject method. Sound impossible? Not if you recall your classic ASP, where you used the Request collections, like Form or QueryString, since you can easily check those values as early as you want. For instance, on my site, I have a drop-down that has an auto-postback, but I don't handle it in the changed event since that is too late -- instead I look for the appropriate Request.Form["xxx"] value.

  • got it! i am using a linkbutton server control, then looking for its ID in the Request.Form["__EVENTARGET"] field, and makeing the switch that way.



    thanks for your help!

  • Paul or Robert W, do you guys have a more verbose example of the dynamic masters implementation?

  • I'm not very good at verbose, but I've got a silly example (not very real-world) in the article download noted above on MSDN (the download has both a v2.0 and a v1.* example). Other than that, my site is of course a fully working real-world demo, and I think all of the pertinent code is actually in the free section, in the template directory.

  • well, i think i'm having the same problem as robert w had. where in my code do i make the template "switch"?

    i have a base MasterPage class defined with the override AddParsedSubObject method per your implementation. i'm still confused though where do i listen for request and how do i change the TemplateFile of the Template control. could you post a little snippet?

    i looked though your msdn whidbey example and still couldn't make sense.

  • The AddParsedSubObject method must go in your page classes, or a common base page -- not in your master template. As for how do you make the switch, something like the following would work if you have a drop-down or cookie that has a client id of master:



    protected override void AddParsedSubObject(object obj) {

    base.AddParsedSubObject(obj);

    if (obj is MasterPage) {

    if (Request["master"] == "alt") {

    ((MasterPage) obj).TemplateFile = "~/AltTemplate.ascx";

    }

    else {

    ((MasterPage) obj).TemplateFile = "~/MainTemplate.ascx";

    }

    }

    }



  • Hi Paul!

    I've joined a project that is still using your master pages and I must say that you did a nice job being about two years before Msoft...

    I'm just wondering about one thing... Is there a way to access the Master Template class from the front-end aspx-page?

    I would like to set some properties on this master template class in order to keep the hiearchy of the layers... Is this posible?

    eg:

    Public Class MenuTest
    Inherits System.Web.UI.Page


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim masterPage As TreoMaster = MasterPageTreo

    ' Here I would like to set some properties to the masterpage class...
    End Sub
    End Class

Comments have been disabled for this content.