Beruflich Dokumente
Kultur Dokumente
Introduction
Over the course of the past five tutorials we have looked at how to create a master page,
define content regions, bind ASP.NET pages to a master page, and define page-specific
content. When a visitor requests a particular content page, the content and master pages'
markup are fused at runtime, resulting in the rendering of a unified control hierarchy.
Therefore, we have already seen one way in which the master page and one of its content
pages can interact: the content page spells out the markup to transfuse into the master
page's ContentPlaceHolder controls.
What we have yet to examine is how the master page and content page can interact
programmatically. In addition to defining the markup for the master page's
ContentPlaceHolder controls, a content page can also assign values to its master page's
public properties and invoke its public methods. Similarly, a master page may interact with
its content pages. While programmatic interaction between a master and content page is
less common than the interaction between their declarative markups, there are many
scenarios where such programmatic interaction is needed.
In this tutorial we examine how a content page can programmatically interact with its
master page; in the next tutorial we will look at how the master page can similarly interact
with its content pages.
Note: Even if you disable the GridView's view state so that it rebinds to its
underlying data source on every postback, it still won't show the just-added record
because the data is bound to the GridView earlier in the page lifecycle than when the
new record is added to the database.
To remedy this so that the just-added record is displayed in the master page's GridView on
postback we need to instruct the GridView to rebind to its data source after the new record
has been added to the database. This requires interaction between the content and master
pages because the interface for adding the new record (and its event handlers) are in the
content page but the GridView that needs to be refreshed is in the master page.
Because refreshing the master page's display from an event handler in the content page is
one of the most common needs for content and master page interaction, let's explore this
topic in more detail. The download for this tutorial includes a Microsoft SQL Server 2005
Express Edition database named NORTHWIND.MDF in the website's App_Data folder. The
Northwind database stores product, employee, and sales information for a fictitious
company, Northwind Traders.
Step 1 walks through displaying the five most recently added products in a GridView in the
master page. Step 2 creates a content page for adding new products. Step 3 looks at how
to create public properties and methods in the master page, and Step 4 illustrates how to
programmatically interface with these properties and methods from the content page.
Note: This tutorial does not delve into the specifics of working with data in ASP.NET.
The steps for setting up the master page to display data and the content page for
inserting data are complete, yet breezy. For a more in-depth look at displaying and
inserting data and using the SqlDataSource and GridView controls, consult the
resources in the Further Readings section at the end of this tutorial.
The next step asks us to specify what database to connect to. Choose the NORTHWIND.MDF
database file from the drop-down list and click Next. Because this is the first time we've
used this database, the wizard will offer to store the connection string in Web.config. Have
it store the connection string using the name NorthwindConnectionString.
Figure 02: Connect to the Northwind Database
The Configure Data Source wizard provides two means by which we can specify the query
used to retrieve data:
Because we want to return just the five most recently added products, we need to specify a
custom SQL statement. Use the following SELECT query:
SELECT TOP 5 ProductName, UnitPrice
FROM Products
ORDER BY ProductID DESC
The TOP 5 keyword returns only the first five records from the query. The Products table's
primary key, ProductID, is an IDENTITY column, which assures us that each new product
added to the table will have a larger value than the previous entry. Therefore, sorting the
results by ProductID in descending order returns the products starting with the most
recently created ones.
Figure 03: Return the Five Most Recently Added Products
After completing the wizard, Visual Studio generates two BoundFields for the GridView to
display the ProductName and UnitPrice fields returned from the database. At this point
your master page's declarative markup should include markup similar to the following:
<asp:Label ID="GridMessage" runat="server"
EnableViewState="false"></asp:Label>
<asp:GridView ID="RecentProducts" runat="server"
AutoGenerateColumns="False"
DataSourceID="RecentProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductName"
HeaderText="ProductName" SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice"
HeaderText="UnitPrice" SortExpression="UnitPrice" />
</Columns>
</asp:GridView>
As you can see, the markup contains: the Label Web control (GridMessage); the GridView
RecentProducts, with two BoundFields; and a SqlDataSource control that returns the five
most recently added products.
With this GridView created and its SqlDataSource control configured, visit the website
through a browser. As Figure 4 shows, you will see a grid in the lower left corner that lists
the five most recently added products.
Figure 04: The GridView Displays the Five Most Recently Added Products
Note: Feel free to clean up the appearance of the GridView. Some suggestions
include formatting the displayed UnitPrice value as a currency and using
background colors and fonts to improve the grid's appearance.
Step 2: Creating a Content Page to Add New
Products
Our next task is to create a content page from which a user can add a new product to the
Products table. Add a new content page to the Admin folder named AddProduct.aspx,
making sure to bind it to the Site.master master page. Figure 5 shows the Solution
Explorer after this page has been added to the website.
Recall that in the Specifying the Title, Meta Tags, and Other HTML Headers in the Master
Page tutorial we created a custom base page class named BasePage that generated the
page's title if it was not explicitly set. Go to the AddProduct.aspx page's code-behind class
and have it derive from BasePage (instead of from System.Web.UI.Page).
Finally, update the Web.sitemap file to include an entry for this lesson. Add the following
markup beneath the <siteMapNode> for the Control ID Naming Issues lesson:
After completing the wizard go to the DetailsView's smart tag and check the "Enable
Inserting" checkbox. This adds a CommandField to the DetailsView with its
ShowInsertButton property set to true. Because this DetailsView will be used solely for
inserting data, set the DetailsView's DefaultMode property to Insert.
That's all there is to it! Let's test this page. Visit AddProduct.aspx through a browser, enter
a name and price (see Figure 6).
Note: In addition to adding some form of visual feedback that the insert has
succeeded, I'd encourage you to also update the DetailsView's inserting interface to
include validation. Currently, there is no validation. If a user enters an invalid value
for the UnitPrice field, such as "Too expensive," an exception will be thrown on
postback when the system attempts to convert that string into a decimal. For more
information on customizing the inserting interface, refer to the Customizing the Data
Modification Interface tutorial from my Working with Data tutorial series.
Because the Label control is implemented as a protected member variable within the master
page it cannot be accessed directly from content pages. In order to work with the Label
within a master page from the content page (or, for that matter, any Web control in the
master page) we need to create a public property in the master page that exposes the Web
control or serves as a proxy by which one of its properties can be accessed. Add the
following syntax to the master page's code-behind class to expose the Label's Text
property:
public string GridMessageText
{
get
{
return GridMessage.Text;
}
set
{
GridMessage.Text = value;
}
}
When a new record is added to the Products table from a content page the
RecentProducts GridView in the master page needs to rebind to its underlying data source.
To rebind the GridView call its DataBind method. Because the GridView in the master page
is not programmatically accessible to the content pages, we need to create a public method
in the master page that, when called, rebinds the data to the GridView. Add the following
method to the master page's code-behind class:
public void RefreshRecentProductsGrid()
{
RecentProducts.DataBind();
}
Note: Don't forget to mark the master page's properties and methods as public. If
you do not explicitly denote these properties and methods as public, they will not
be accessible from the content page.
There are two ways that a content page can programmatically interface with its master
page:
Using the Page.Master property, which returns a loosely-typed reference to the master
page, or
Specify the page's master page type or file path via a @MasterType directive; this
automatically adds a strongly-typed property to the page named Master.
Now that we have casted the loosely-typed Page.Master property to the Site type we can
reference the properties and methods specific to Site. As Figure 7 shows, the public
property GridMessageText appears in the IntelliSense drop-down.
Figure 07: IntelliSense Shows our Master Page's Public Properties and Methods
Note: If you named your master page file MasterPage.master then the master
page's code-behind class name is MasterPage. This can lead to ambiguous code
when casting from the type System.Web.UI.MasterPage to your MasterPage class.
In short, you need to fully qualify the type you are casting to, which can be a little
tricky when using the Web Site Project model. My suggestion would be to either
make sure that when you create your master page you name it something other
than MasterPage.master or, even better, create a strongly-typed reference to the
master page.
Use the @MasterType directive to inform the ASP.NET engine of the content page's
master page type. The @MasterType directive can accept either the type name of the
master page or its file path. To specify that the AddProduct.aspx page uses Site.master
as its master page, add the following directive to the top of AddProduct.aspx:
<%@ MasterType VirtualPath="~/Site.master" %>
This directive instructs the ASP.NET engine to add a strongly-typed reference to the master
page through a property named Master. With the @MasterType directive in place, we can
call the Site.master master page's public properties and methods directly through the
Master property without any casts.
Note: If you omit the @MasterType directive, the syntax Page.Master and Master
return the same thing: a loosely-typed object to the page's master page. If you
include the @MasterType directive then Master returns a strongly-typed reference to
the specified master page. Page.Master, however, still returns a loosely-typed
reference. For a more thorough look at why this is the case and how the Master
property is constructed when the @MasterType directive is included, see K. Scott
Allen's blog entry @MasterType in ASP.NET 2.0.
Figure 8 shows the AddProduct.aspx page immediately after a new product - Scott's Soda -
has been added to the database. Note that the just-added product name is noted in the
master page's Label and that the GridView has been refreshed to include the product and its
price.
Figure 08: The Master Page's Label and GridView Show the Just-Added Product
Summary
Ideally, a master page and its content pages are completely separate from one another and
require no level of interaction. While master pages and content pages should be designed
with that goal in mind, there are a number of common scenarios in which a content page
must interface with its master page. One of the most common reasons centers around
updating a particular portion of the master page display based on some action that
transpired in the content page.
The good news is that it's relatively straightforward to have a content page
programmatically interact with its master page. Start by creating public properties or
methods in the master page that encapsulate the functionality that needs to be invoked by
a content page. Then, in the content page, access the master page's properties and
methods through the loosely-typed Page.Master property or use the @MasterType directive
to create a strongly-typed reference to the master page.
In the next tutorial we examine how to have the master page programmatically interact
with one of its content pages.
Happy Programming!
Further Reading
For more information on the topics discussed in this tutorial, refer to the following
resources:
Special Thanks To
This tutorial series was reviewed by many helpful reviewers. Lead reviewer for this tutorial
was Zack Jones. Interested in reviewing my upcoming MSDN articles? If so, drop me a line
at mitchell@4GuysFromRolla.com