Main ASP .NET Tutorial Index 

Tutorial 3: Master Pages and Site Navigation

 

Scott Mitchell

June 2006

Download the ASPNET_Data_Tutorial_3_CS.exe sample code.

Contents of Tutorial 3 (Visual C#)

Introduction
Step 1: Creating the Master Page
Step 2: Adding a Homepage to the Web Site
Step 2: Creating a Site Map
Step 3: Displaying a Menu Based on the Site Map
Step 4: Adding Breadcrumb Navigation
Step 5: Adding the Default Page for Each Section
Summary

Step 3: Displaying a Menu Based on the Site Map

Accessing data in ASP.NET 2.0 can be accomplished programmatically, like in ASP.NET 1.x, or declaratively, through the new data source controls. There are several built-in data source controls such as the SqlDataSource control, for accessing relational database data, the ObjectDataSource control, for accessing data from classes, and others. You can even create your own custom data source controls.

The data source controls serve as a proxy between your ASP.NET page and the underlying data. In order to display a data source control's retrieved data, we'll typically add another Web control to the page and bind it to the data source control. To bind a Web control to a data source control, simply set the Web control's DataSourceID property to the value of the data source control's ID property.

To aid in working with the site map's data, ASP.NET includes the SiteMapDataSource control, which allows us to bind a Web control against our website's site map. Two Web controls – the TreeView and Menu – are commonly used to provide a navigation user interface. To bind the site map data to one of these two controls, simply add a SiteMapDataSource to the page along with a TreeView or Menu control whose DataSourceID property is set accordingly. For example, we could add a Menu control to the master page using the following markup:

<div id="navigation">
    <asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1">
    </asp:Menu>
    
    <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />
</div>

For a finer degree of control over the emitted HTML, we can bind the SiteMapDataSource control to the Repeater control, like so:

<div id="navigation">
    <ul>
        <li><asp:HyperLink runat="server" ID="lnkHome" NavigateUrl="~/Default.aspx">Home</asp:HyperLink></li>
        
        <asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1">
            <ItemTemplate>
                <li>
                    <asp:HyperLink runat="server" NavigateUrl='<%# Eval("Url") %>'><%# Eval("Title") %></asp:HyperLink>
                </li>
            </ItemTemplate>
        </asp:Repeater>
    </ul>
    
    <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" ShowStartingNode="false" />
</div>

The SiteMapDataSource control returns the site map hierarchy one level at a time, starting with the root site map node (Home, in our site map), then the next level (Basic Reporting, Filtering Reports, and Customized Formatting), and so on. When binding the SiteMapDataSource to a Repeater, it enumerates the first level returned and instantiates the ItemTemplate for each SiteMapNode instance in that first level. To access a particular property of the SiteMapNode, we can use Eval(propertyName), which is how we get each SiteMapNode's Url and Title properties for the HyperLink control.

The Repeater example above will render the following markup:

<li>
    <a href="/Code/BasicReporting/Default.aspx">Basic Reporting</a>
</li>

<li>
    <a href="/Code/Filtering/Default.aspx">Filtering Reports</a>
</li>

<li>
    <a href="/Code/CustomFormatting/Default.aspx">Customized Formatting</a>
</li>

These site map nodes (Basic Reporting, Filtering Reports, and Customized Formatting) comprise the second level of the site map being rendered, not the first. This is because the SiteMapDataSource's ShowStartingNode property is set to False, causing the SiteMapDataSource to bypass the root site map node and instead begin by returning the second level in the site map hierarchy.

To display the children for the Basic Reporting, Filtering Reports, and Customized Formatting SiteMapNodes, we can add another Repeater to the initial Repeater's ItemTemplate. This second Repeater will be bound to the SiteMapNode instance's ChildNodes property, like so:

<asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1">
    <ItemTemplate>
        <li>
            <asp:HyperLink runat="server" NavigateUrl='<%# Eval("Url") %>'><%# Eval("Title") %></asp:HyperLink>

            <asp:Repeater runat="server" DataSource='<%# ((SiteMapNode) Container.DataItem).ChildNodes %>'>
                <HeaderTemplate>
                    <ul>
                </HeaderTemplate>
                
                <ItemTemplate>
                    <li>
                        <asp:HyperLink runat="server" NavigateUrl='<%# Eval("Url") %>'><%# Eval("Title") %></asp:HyperLink>
                    </li>
                </ItemTemplate>
                
                <FooterTemplate>
                    </ul>
                </FooterTemplate>
            </asp:Repeater>
        </li>
    </ItemTemplate>
</asp:Repeater>

These two Repeaters result in the following markup (some markup has been removed for brevity):

<li>
    <a href="/Code/BasicReporting/Default.aspx">Basic Reporting</a>
    <ul>
       <li>
          <a href="/Code/BasicReporting/SimpleDisplay.aspx">Simple Display</a>
       </li>
       <li>
          <a href="/Code/BasicReporting/DeclarativeParams.aspx">Declarative Parameters</a>
       </li>
       <li>
          <a href="/Code/BasicReporting/ProgrammaticParams.aspx">Setting Parameter Values</a>
       </li>
    </ul>
</li>

<li>
    <a href="/Code/Filtering/Default.aspx">Filtering Reports</a>
    ...
</li>

<li>
    <a href="/Code/CustomFormatting/Default.aspx">Customized Formatting</a>
    ...
</li>

Using CSS styles chosen from Rachel Andrew's book The CSS Anthology: 101 Essential Tips, Tricks, and Hacks, the <ul> and <li> elements are styled such that the markup produces the following visual output:

Figure 11. A Menu Composed from Two Repeaters and Some CSS

This menu is in the master page and bound to the site map defined in Web.sitemap, meaning that any change to the site map will be immediately reflected on all pages that use the Site.master master page.

Disabling ViewState

All ASP.NET controls can optionally persist their state to the view state, which is serialized as a hidden form field in the rendered HTML. View state is used by controls to remember their programmatically-changed state across postbacks, such as the data bound to a data Web control. While view state permits information to be remembered across postbacks, it increases the size of the markup that must be sent to the client and can lead to severe page bloat if not closely monitored. Data Web controls – especially the GridView – are particularly notorious for adding dozens of extra kilobytes of markup to a page. While such an increase may be negligible for broadband or intranet users, view state can add several seconds to the round trip for dial-up users.

To see the impact of view state, visit a page in a browser and then view the source sent by the web page (in Internet Explorer, go to the View menu and choose the Source option). You can also turn on page tracing to see the view state allocation used by each of the controls on the page. The view state information is serialized in a hidden form field named __VIEWSTATE, located in a <div> element immediately after the opening <form> tag. View state is only persisted when there is a Web Form being used; if your ASP.NET page does not include a <form runat="server"> in its declarative syntax there won't be a __VIEWSTATE hidden form field in the rendered markup.

The VIEWSTATE form field generated by the master page adds roughly 1,800 bytes to the page's generated markup. This extra bloat is due primarily to the Repeater control, as the contents of the SiteMapDataSource control are persisted to view state. While an extra 1,800 bytes may not seem like much to get excited about, when using a GridView with many fields and records, the view state can easily swell by a factor of 10 or more.

View state can be disabled at the page or control level by setting the EnableViewState property to false, thereby reducing the size of the rendered markup. Since the view state for a data Web control persists the data bound to the data Web control across postbacks, when disabling the view state for a data Web control the data must be bound on each and every postback. In ASP.NET version 1.x this responsibility fell on the shoulders of the page developer; with ASP.NET 2.0, however, the data Web controls will rebind to their data source control on each postback if needed.

To reduce the page's view state let's set the Repeater control's EnableViewState property to false. This can be done through the Properties window in the Designer or declaratively in the Source view. After making this change the Repeater's declarative markup should look like:

<asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1" EnableViewState="False">
    <ItemTemplate>
        ... ItemTemplate contents omitted for brevity ...
    </ItemTemplate>
</asp:Repeater>

After this change, the page's rendered view state size has shrunk to a mere 52 bytes, a 97% savings in view state size! In the tutorials throughout this series we'll disable the view state of the data Web controls by default in order to reduce the size of the rendered markup. In the majority of the examples the EnableViewState property will be set to false and done so without mention. The only time view state will be discussed is in scenarios where it must be enabled in order for the data Web control to provide its expected functionality.

 

 

 

 

 

Go to top or next part of tutorial

 
eXTReMe Tracker