Introduction
When graphic designers create a layout for a Web site, they typically break down page layout into distinct regions, such as a common header that includes the logo and various navigation links, a left
One common challenge that faces page developers using Master Pages is how to pass information from a Master Page to its content page, or vice-a-versa. A Master Page might contain a DropDownList control that when its selected index changes, the corresponding content page needs to have its display updated. Or perhaps some action in the content page needs to update the appearance of the Master Page. In this article we will explore techniques for passing information between a Master Page and its content page. Read on to learn more!
Master Page and Content Page Design Basics
As a website grows, new content pages will no doubt be added and associated with existing Master Pages. Or existing content pages that use MasterPage X might be updated to use Master Page Y instead. Consequently, it's smart to design your Master Pages so that there is no reliance on their content pages. That is, a Master Page shouldn't assume that its content page possesses a particular method or that it has a specific set of Web controls defined in its declarative markup. Such a Master Page is said to be loosely-coupled with its content page; if the Master Page expects particular methods or Web controls on its corresponding contnet pages, then it's said to be tightly-coupled.
It's possible for a Master Page to be loosely-coupled to its content page, but for the content page to be tightly-coupled to the Master Page. For example, the Master Page may make no assumptions and not base any functionality on its content page's methods or markup, but the page may assume that the Master Page has a particular method or Web control present. Ideally, a page will be loosely-coupled with its Master Page, but if information needs to be passed between a Master Page and its content page (or vice-a-versa), there must be some level of coupling, either from the Master Page to the content page (bad) or the content page to the Master Page (better). The demos in this article establish a tight-coupling from the content pages to their Master Pages, but retain a loose-coupling from the Master Pages to their content pages.
Why the Tight-Coupling From the Content Page to Master Page is Preferred |
---|
You may be wondering why I recommend a coupling from the content page to the Master Page rather than the other way around. When there exists a coupling from A to B, modifying B typically requires a corresponding modification to A. Since I expect that most websites have far fewer Master Pages than content pages, and that Master Pages are less likely to have their tightly-coupled interface modified than in a content page, from a logistics standpoint it makes sense to introduce the coupling from content pages to Master Pages. Secondarily, this coupling direction just feels "better". When associating a new web page with an existing Master Page, it seems funky to have to stop and think, "OK, now what functionality does this page need to expose in order to work with this Master Page?" If this is a triffle confusing, don't sweat it. I think once you explore the demos the coupling issue will make more sense. |
Passing Information from a Content Page to its Master Page
A Master Page can expose its Web controls or methods directly to its content pages. To expose a method, simply mark it as public from the Master Page's code-behind class:
' VB... |
Here DataFromPage
is a Label Web control in the Master Page's declarative markup. The method DisplayDataFromPage
is a public method that can be invoked from the Master Page's content page. The string input passed into this method is assigned to the Label Web control's Text
property. Using this method, the Master Page's content page can set the text displayed in the DataFromPage
Label.
Additionally, the Master Page can expose a Web control from its declarative syntax as a read-only property:
' VB... |
To access the Master Page's methods or properties from a content page, reference the Master Page through the Page.Master
property. This property returns an object of type MasterPage
, so you'll need to explicitly cast it to the appropriate type before calling its methods or referencing its properties. Alternatively, you can set the @MasterType
directive, which adds a property to the auto-generated ASP.NET code-behind class code named Master
that is a strongly-typed reference to the specified Master Page.
The following markup in the .aspx
file for the content page spells out the Master Page type:
<%@ MasterType VirtualPath="pathToMasterPage" %> |
With this directive added (and the .aspx
file saved), you can reference the Master Page's public methods and properties programmatically in the content page's code-behind class using Master
like so:
' VB... |
The code shown here, which is available from the download at the end of this article, is from a content page that includes a TextBox named TextToShowInMasterPage
and a Button Web control named ShowText
. When the ShowText
Button is clicked, the Master Page's DisplayDataFromPage
method is called, passed the value of the TextBox's Text
property. (Alternatively, the Master Page's DataFromPageLabelControl
property could be used to reference the Label control in the Master Page, at which point its Text
property could be set.) The net result is that entering text into the TextBox on the content page and clicking the Button results in the Master Page's display updating to display this user-entered text.
Page.Master versus Master |
---|
If you use the @MasterType directive to create a strongly-typed Master Page reference in the content page's code-behind class, you must use Master to get a strongly-typed reference. If your code uses Page.Master , you'll get a loosely-typed reference (one whose type is MasterPage and, therefore, requires a cast before the specific Master Page's members can be accessed). |
Passing Information from a Master Page to its Content Page
In certain scenarios a Master Page might contain a Web control that, when modified by a user in some fashion, needs to update the corresponding content page. Perhaps the Master Page contains a DropDownList control that, when modified, needs to refresh the page and display data based on the selected item. Personally, I think such functionality should be moved to a separate User Control and that User Control added to the ASP.NET content pages, as needed. However, you may have a situation in which it is imperative that the Master Page pass information to a content page, so it's worth exploring techniques for accomplishing this.
As aforementioned, I'd strongly encourage you to not establish a tight-coupling from the Master Page to the content page. Such a tight-coupling can be avoided by having the Master Page not directly call some content page method, but instead through the judicious use of events. In short, we can create a Master Page that raises an event when some action happens on the Master Page side. This event can pass any additional information needed and then content pages that require this information can "subscribe" to the Master Page's event. (An alternate approach, not explored in this article, is to have the content page assign a delegate to a property in the Master Page. This technique is discussed in Tim Stall's article, Trigger Page Methods from a User Control. While Tim's article deals with User Controls and ASP.NET pages, the concepts apply equally to Master Pages (the User Control) and content pages (the ASP.NET page).)
To illustrate this, imagine that we had a DropDownList in the Master Page. When its selected index changes, we want to notify the content page of the change so that it can update its display accordingly. Start by creating a DropDownList named Moods
in the Master Page that lists various moods (Happy, Sad, etc.) and then create an event handler for this DropDownList's SelectedIndexChanged
event. Next, we need to define an event for the Master Page, specifying the event handler signature. The event handler signature specifies what input parameters are passed to the event handler. For this example, let's pass the selected DropDownList item's Text
and Value
property values. Therefore, we can use the CommandEventHandler
delegate, which passes a CommandEventArgs
object to the event handler, which includes CommandName
and CommandArgument
properties that we can use to hold the selected ListItem
's Text
and Value
property values.
To define an event named MoodChanged
for the Master Page that uses the CommandEventHandler
delegate, use the following syntax:
' VB... |
When the DropDownList's SelectedIndexChanged
event fires we want to raise the Master Page's MoodChanged
event. To accomplish this, add the following code into the DropDownList's SelectedIndexChanged
event handler:
' VB... |
Note: In C#, we have to first check that the event is not null
before raising it (note the "&& MoodChanged != null
" check in the if
statement). This is because the event reference is null
if no one has subscribed to the event...
The last step is "subscribing" to the event in the content page's that care about the changing of this DropDownList. To subscribe to the event we need to programmatically specify in the content page that when the Master Page's MoodChanged
event fires that a particular event handler defined in the content page should execute. The following code illustrates how to establish this binding between the event and the event handler in C# and VB:
' VB... |
The Page_Init
event handler associates the Master Page's MoodChanged
event with the event handler MoodChangedFromMasterPage
(and must re-establish this association on each and every postback). Note that the Master
property provides a strongly-typed experience. This is because I used the @MasterType
directive in the .aspx
file for this content page.
Conclusion
Ideally Master Pages and their content pages will be completely independent entities with no need to share information. However, there are scenarios where this cannot be avoided without difficulty. For those situations, there are various techniques, as discussed in this article. To pass information from a content page to a Master Page, the content page can get a strongly-typed reference to the Master Page through use of the @MasterType
directive. This allows the Master Page's public methods and properties to be invoked from content pages with the benefits of compile-time type checking. When passing information from the Master Page to its content pages, I've found the best approach is to have the Master Page raise an event and pass the necessary information. Those pages that need to be notified, then, can subscribe to this event.