Click here to Skip to main content
15,884,836 members
Articles / Web Development / ASP.NET

How to manipulate master page controls in ASP.NET when output caching is enabled

Rate me:
Please Sign up or sign in to vote.
4.68/5 (14 votes)
21 Aug 2012CPOL7 min read 78.7K   802   21   7
This short article shows how to manipulate master page controls in ASP.NET when output caching is enabled.

Table of Contents

  • Introduction
  • Most easy way out
  • What will happen when control has output cache defined?
  • What is the solution then?
  • A good suggestion
  • Problem: Adding control back in Page.Controls creates server error
  • Problem: Setting other properties of the control other than visibility
  • Conclusion
  • References

Development Platform

  • Visual Studio 2010
  • Microsoft .NET Framework 4.0
  • IIS 5.1~7

Introduction

In this short article we are going to see how we can use some smart tricks for manipulating controls in the master page of an ASP.NET website from a content page. The topic seems a little bit lame and should be pretty straightforward but I wouldn’t be discussing with the community unless it has a very unexpected turn.

Image 1

Here is the situation, we have an ASP.NET 4.0 web application with a single Master page. In some cases we need to hide the master page’s controls, in particular the header and footer.

Image 2

To demonstrate the ideas and story I have created a simple ASP.NET 4.0 web application in Visual Studio. The Solution Explorer components are given above as a screenshot, nothing special, just taken after creating the project.

“Site.Master” the site layout HTML given below:

XML
<body>
    <form runat="server">
        <div class="page">
            <uc1:Header ID="Header1" runat="server" />
            <div class="main">
                <asp:ContentPlaceHolder ID="MainContent" runat="server" />
            </div>
            <div class="clear">
            </div>
        </div>
        <uc2:Footer ID="Footer1" runat="server" />
    </form>
</body>

Looks pretty innocent, right? In fact most of our website templates have a header, main-content, footer in a generic style. Now then the requirement is to hide the header and footer in one of the content pages, in fact in a couple of pages. Those pages are report pages and it is not necessary to show the header and footer as those pages can be printed.

Most easy way out

Most easy way out is just use the FindControl method in the page.master object and then simply hide them. We can also cast them to a custom control and then set the properties. This will work in 99% cases and is really simple. This can be found in net with a Google search, and the first result will do the job. Below is the code for the most straightforward way.

C#
public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var masterPage = Master;
        if (masterPage != null)
        {
            masterPage.FindControl("Header1").Visible = false;
            masterPage.FindControl("Footer1").Visible = false;
        }
    }
}

Yes, just like that, and our site’s header and footer will open the rules of visibility. Now we are here because we have a high performance website and it has no view state, no session, whatsoever. All login and security measures depend on cookies and each request.

Moreover we have output caching enabled on the header and footer for “9000”.

What will happen when control has output cache defined?

I am sure all of you know that you can cache a page, user control's output cache, it’s a standard ASP.NET attribute and can be added in any page or user control to cache the output for an object for a particular period of time with some parameters or customization. To do so we have to add <%@ OutputCache Duration="9999" VaryByParam="none" %> to the control or page declaration in HTML. To know more about it, please visit http://msdn.microsoft.com/en-us/library/hdxfb6cy(v=vs.71).aspx.

What happens to the above code when we run the application in Debug mode? The page works fine for the first time. What would happen if we try to access it again? Just hit Enter in the browser or refresh the browser page, crap! It is not working, showing a server error and it says the master page does not have this control and eventually throws a server error.

Image 3

If you check if the control exists or not, the first time it will return yes, the second time it will say it does not exist. It is normal and happens because the controls that we are trying to modify are cached, and hence can’t be accessed like that.

What is the solution then?

The solution is to find the cached control and then work on that control and finally add it to the control again so that whatever we have changed is reflected on the page's final output. Below given is the code segment for this. This time we have modified the code and kept it in the master page, and just invoked a method from the content page.

Master page’s code

C#
using System;
using System.Web.UI;
using MasterPageControlExample.Controls;

namespace MasterPageControlExample
{
public partial class SiteMaster : MasterPage
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    public void DisplayHeader(bool value)
    {
        if(Header1!=null)
            Header1.Visible = value;
        else
        {
            var cachedHeader = (PartialCachingControl) Page.LoadControl(@"Controls\Header.ascx");
            Page.Controls.Add(cachedHeader);
            if(cachedHeader.CachedControl !=null)
            {
                var header = cachedHeader.CachedControl as Header;
                if (header != null) header.Visible = value;
            }
        }
    }

    public void DisplayFooter(bool value)
    {
        if (Footer1 != null)
            Footer1.Visible = value;
        else
        {
            var cachedFooter = (PartialCachingControl)LoadControl(@"Controls\Footer.ascx");
            Page.Controls.Add(cachedFooter);
            if (cachedFooter.CachedControl != null)
            {
                var footer = cachedFooter.CachedControl as Footer;
                if (footer != null) footer.Visible = value;
            }
        }
    }
}
}

Content page’s code

C#
using System;

namespace MasterPageControlExample
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var masterPage = Master;
            if (masterPage != null)
            {
                var siteMaster = (masterPage as SiteMaster);
                if (siteMaster != null) siteMaster.DisplayHeader(false);
            }
        }
    }
}

In the above master page code we first check if the control exists or not by simply checking for null. The first time it won't be null and can directly assign the properties. The second and subsequent times until the control is created once again when the cache expires, the else block of the code will be executed.

In the else block of the DisplayHeader or DisplayFooter method, we have loaded the control. This time the Page.LoadControl method will return a PartialCachingControl object. It has to be again added to Page.Controls to be able to be accessed from the CachedControl property of the PartialCachingControl object. Finally we add the required properties value.

Now this code will work on 100% case as we have over came the problem with output cache as well.

A good suggestion

Even if you are a beginner you probably know it but still sharing this as some of us might not have implemented it. I am talking about the PageBase and ControlBase objects for an ASP.NET site. The idea is just to reuse some repeating code and other cross cutting concerns if we have any in our page life cycle. Here I suggest that you create a BasePage.cs class where you will encapsulate code for displaying header and footer, and in the content page, you will just call these methods, and in that case your common code will all be in one place.

Below is the code for BasePage.cs:

BasePage’s code

C#
namespace MasterPageControlExample.Objects
{
    public class BasePage:System.Web.UI.Page
    {
        protected void DisplayHeader(bool value)
        {
            var masterPage = Master;
            if (masterPage != null)
            {
                var siteMaster = (masterPage as SiteMaster);
                if (siteMaster != null) siteMaster.DisplayHeader(value);
            }
        }

        protected void DisplayFooter(bool value)
        {
            var masterPage = Master;
            if (masterPage != null)
            {
                var siteMaster = (masterPage as SiteMaster);
                if (siteMaster != null) siteMaster.DisplayFooter(value);
            }
        }
    }
}

Content page’s code

C#
using System;
using MasterPageControlExample.Objects;

namespace MasterPageControlExample
{
    public partial class _Default : BasePage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            DisplayHeader(false);
        }
    }
}

This looks compact and organized now, when we manipulate some cached control, it tends to create some inherent problems, some of the problems are discussed below.

Problem: Adding a control back in Page.Controls creates a server error

We can’t add the cached control in the Page.Controls collection, since there is a good chance that controls such as the header and footer will have some tags that have server controls and that’s going to create problems while adding those controls in to the page.

Result of adding header back to Page.Controls

Image 4

Then what is the solution? We have to add in a placeholder where for this master page, the markup needs to change so that header and footer will be declared declaratively inside a place holder.

Modified master page code

ASP.NET
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" 
Inherits="MasterPageControlExample.SiteMaster" %>

<%@ Register Src="Controls/Header.ascx" TagName="Header" TagPrefix="uc1" %>
<%@ Register Src="Controls/Footer.ascx" TagName="Footer" TagPrefix="uc2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head runat="server">
    <title></title>
    <link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
    <asp:ContentPlaceHolder ID="HeadContent" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form runat="server">
    <div class="page">
        <asp:PlaceHolder ID="PlaceHolderHeader" runat="server">
            <uc1:Header ID="Header1" runat="server" />
        </asp:PlaceHolder>
        <div>
            <h2>
                <a href="Default.aspx">Default.aspx</a><br />
                <a href="About.aspx">About.aspx</a><br />
                <a href="Report.aspx">Report.aspx</a><br />
            </h2>
        </div>
        <div class="main">
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />
        </div>
        <div class="clear">
        </div>
    </div>
    <asp:PlaceHolder ID="PlaceHolderFooter" runat="server">
        <uc2:Footer ID="Footer1" runat="server" />
    </asp:PlaceHolder>
    </form>
</body>
</html>

Note that in the above code we added two place holders to hold the header and footer, and the header and footer are user controls added inside the place holder. Now if our intention is just to modify the visibility status there is a very easy way out again.

Here is the situation, we have three content pages:

  • Default page [where we want to display both header and footer]
  • About page [where we want to display only header and hide footer]
  • Report page [where we want to hide both header and footer]

Content page’s code in this scenario

C#
//first page
public partial class _Default : BasePage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        DisplayHeader(true);
        DisplayFooter(true);
    }
}
//second page
public partial class About : BasePage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        DisplayHeader(true);
        DisplayFooter(false);
    }
}
//third page
public partial class Report : BasePage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        DisplayHeader(false);
        DisplayFooter(false);
    }
}

Master page’s code

C#
public void DisplayHeader(bool value)
{
    if(Header1!=null)
        Header1.Visible = value;
    else
    {
        Page.LoadControl(@"Controls\Header.ascx");
        var cachingControl = PlaceHolderHeader.Controls[0] as StaticPartialCachingControl;
        if (cachingControl != null) cachingControl.Visible = value;
    }
}

public void DisplayFooter(bool value)
{
    if (Footer1 != null)
        Footer1.Visible = value;
    else
    {
        LoadControl(@"Controls\Footer.ascx");
        var cachingControl = PlaceHolderFooter.Controls[0] as StaticPartialCachingControl;
        if (cachingControl != null) cachingControl.Visible = value;
    }
}

Note that in this case I have cast control[0] as the place holder as we have only one control inside the placeholder. Also note that you can’t set the custom properties here. As it’s a StaticPartialCachingControl.

Problem: Setting other properties of the control other than visibility

Well this is the final part of the article in this section. We want to set other properties of the cached control, unfortunately it is not that easy and there is not an easy way out for setting properties in a cached control. Rather we have to load the control and then add it back in the Controls collection, but before changing the desired property.

The intention is to change the site title in the About.aspx page. I am going to skip the rest of the details and list the code for the master page. That will be self explanatory.

Master page’s code for setting site title

C#
public void SetSiteTitle(string siteTitle)
{
   if (Header1 != null)
       Header1.SiteTitle = siteTitle;
   else
   {
       var cachedHeader = (PartialCachingControl)LoadControl(@"Controls\Header.ascx");
       PlaceHolderHeader.Controls.Clear();
       PlaceHolderHeader.Controls.Add(cachedHeader);
       if (cachedHeader.CachedControl != null)
       {
           var header = cachedHeader.CachedControl as Header;
           if (header != null) header.SiteTitle = siteTitle;
       }
   }
}

There are a lot of things that I want to list about this tiny little article but keeping away some things for the future. I hope I would get good feedback for this article.

Conclusion

In this short article we have seen a particular problem’s most easy way out, followed by what will happen when caching is enabled for a control, and finally some tips and tricks to make it perfect. The knowledge is available in the web and can be found easily with a single search in any search engine, however for beginners it’s a panic situation, and I have tried to organize the ideas sequentially so that it make sense in first read for an absolute beginner.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Riteq
Australia Australia
About Md. Masudur Rahman

Masudur currently works at Riteq as a software developer. Masudur Lives in Sydney, Australia.

Awards

26 May 2009: Monthly competition: Best ASP.NET article of April 2009

24 Mar 2009: Monthly competition: Best ASP.NET article of February 2009

Masudur Blog

Masudur put down his interesting learning experiences in his blog at http://munnaondotnet.blogspot.com/.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Abhishek Hotte27-Jun-14 3:08
Abhishek Hotte27-Jun-14 3:08 
SuggestionNice Article!! Pin
Divya Sagi25-Dec-12 22:48
Divya Sagi25-Dec-12 22:48 
GeneralMy vote of 5 Pin
Аslam Iqbal16-Sep-12 18:51
professionalАslam Iqbal16-Sep-12 18:51 
GeneralRe: My vote of 5 Pin
Rahman Masudur16-Sep-12 18:53
Rahman Masudur16-Sep-12 18:53 
GeneralRe: My vote of 5 Pin
Аslam Iqbal16-Sep-12 18:56
professionalАslam Iqbal16-Sep-12 18:56 
GeneralMy vote of 5 Pin
Shahriar Hyder22-Aug-12 19:31
Shahriar Hyder22-Aug-12 19:31 
GeneralRe: My vote of 5 Pin
Rahman Masudur23-Aug-12 1:50
Rahman Masudur23-Aug-12 1:50 
Thanks a lot
Thanks & Best of Luck
Md. Masudur Rahman
http://munnaondotnet.blogspot.com/
http://www.shatkotha.com

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.