Capture Master Page Events in Content Pages - ASP.NET 2.0
Using Delegates with Master Page Events:
For newer developers, using events and delegates can sometime seem pretty intimidating. This article is aimed to give a very simple example of using events, delegates, public properties and subscribing forms to capture events from one form to the next.
The problem: A challenge developers may have using ASP.NET 2.0's master pages and content pages is the ability to notify content pages that something has happened in the master page that may be of interest inside the content page. For this example, let's assume our master page holds a list of buttons, and your content page needs to be notified that a new item is selected. If you tried to assign the menu value to a public property and then check this value in the Page_Load event of the content page, you'd be dissapointed by the results. Why? The Page Load of the content page will fire before the Master Page's Button Click event, so the public property won't be changed yet. For new developers, this is the point you curse the new technology and start wondering if your local pub is hiring bartenders. Don't despair, you've got this handled:
The solution: To solve this, we'll create a delegate. Add this outside of the code behind class to expose it to the content forms. Think of a delegate as a notification system that is avaiable for all to use. Let's think of this as the Ding! download from Southwest Airlines.
public delegate void MasterPageMenuClickHandler(object sender, System.EventArgs e);
We'll also create a public event we'll use to reference this button click event (this is inside the code behind class):
public event MasterPageMenuClickHandler MenuButton;
When the Master Page's button is clicked, its regular click event fires. Inside this event, the delegate is called to tell any subscribing entities that something great has happened, and they should react. I like to think of this action as the Southwest Airlines adding a new flight that should be broadcast to the Ding! users. Here's how our master page is set up:
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<link href="StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div id="wrapper">
<div id="header">
Header Area
</div>
<div id="menuArea">
<asp:Button ID="btnMenu1" runat="server" Text="Menu 1" OnClick="btnMenu1_Click" />
<asp:Button ID="btnMenu2" runat="server" Text="Menu 2" OnClick="btnMenu2_Click" />
</div>
<div id="mainContent">
<asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
</asp:contentplaceholder>
</div>
<div id="footer">
Footer Area
</div>
</div>
</form>
</body>
</html>
And the code behind class for the Master Page:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
#region Public Delegates
// Expose delegates for Master Page Events
public delegate void MasterPageMenuClickHandler(object sender, System.EventArgs e);
#endregion Public Delegates
public partial class MasterPage : System.Web.UI.MasterPage
{
#region Public Properties
private string _currentButton;
public string CurrentButton
{
get { return _currentButton; }
set { _currentButton = value; }
}
#endregion Public Properties
#region Public Events
public event MasterPageMenuClickHandler MenuButton;
#endregion Public Events
#region Page Events
/// <summary>
/// Handles Page Load Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// Handles Button 1 Click Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnMenu1_Click(object sender, EventArgs e)
{
// Assign value to public property
_currentButton = "One";
// Fire event to existing delegates
OnMenuButton(e);
}
/// <summary>
/// Handles Button 2 Click Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnMenu2_Click(object sender, EventArgs e)
{
// Assign value to public property
_currentButton = "Two";
// Fire event to existing delegates
OnMenuButton(e);
}
#endregion Page Events
#region Virtual Methods
/// <summary>
/// Invokes subscribed delegates to menu click event.
/// </summary>
/// <param name="e">Click Event arguments</param>
protected virtual void OnMenuButton(EventArgs e)
{
if (MenuButton != null)
{
//Invokes the delegates.
MenuButton(this, e);
}
}
#endregion Virtual Methods
}
Now, subscribing to this delegate is easy. In the Page Load of the content page, we simply add an event handler to the public event you created in the master page:
Master.MenuButton +=new MasterPageMenuClickHandler(Master_MenuButton);
One important item to note: to reference Master in this fashion, you'll have to add a MasterType directive to the top of the content page's aspx page.
<%@ MasterType virtualPath="~/MasterPage.master"%>
To complete our airline thought, this is similar to your sister downloading the Ding! software onto your machine.
So here's the process:
A new flight is made available (Master Page button click)
The Ding! service is told about the new item (Our delegate is called by the button click event)
The service tells your computer's Ding! client to show the flight (The subscriber is notified)
You book a flight to Norway (Your form handles the event and gets new value from master page's public property.
Delegates are so useful because they allow a class to notify others of an event, without being tied in any way to the subscribing forms. Here's the default.aspx content page code behind:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page
{
/// <summary>
/// Handles Page Load Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
Master.MenuButton +=new MasterPageMenuClickHandler(Master_MenuButton);
}
/// <summary>
/// Reacts to Master Page menu click event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Master_MenuButton(object sender, EventArgs e)
{
lblOutput.Text = string.Format("Button {0} was pressed", Master.CurrentButton.ToString());
}
}
Pretty simple explanation, I hope. Sometimes it just takes a basic example to get you thinking about a process correctly...hopefully this will push you towards answering the issue that brought you here. You can download my simple example here. If you work for Southwest, I do accept PayPal <g>
-steve
Here are some other articles that may help you out:
MSDN events
MSDN 2 events
newsgroup discussion