Below is shown the general layout for all of the pages of the eCommerce Web site. It is based on the use of a master page to present the common elements, which include the banner across the top of the page and the site menus down the left. Different content appears in the right-hand section of the page when menu links are clicked or when performing book searches.
Code to set up the eCommerce.master page is shown in Listing 13-1. The entire page is formatted inside a table with cells containing the site title, the site menu and book search sections, and an <asp:ContentPlaceHolder> control defining a content section for display of site pages. The script section is described below.
<%@ Master %> <SCRIPT Runat="Server"> ... </SCRIPT> <html> <head Runat="Server"> <title>webWarehouse.com</title> <link href="stylesheetEC.css" rel="stylesheet"> </head> <body> <form Runat="Server"> <table id="MasterTable" border="0" cellspacing="0" width="100%"> <tr> <td id="HeaderCell" colspan="2"> webWarehouse.com </td> </tr> <tr> <td id="CounterCell" colspan="2"> <!-- Visitor Counter --> <asp:AccessDataSource id="CounterSource" Runat="Server" DataFile="../Databases/BooksDB.mdb" SelectCommand="SELECT VisitorCounter FROM Counters"/> <asp:FormView id="VisitorCount" DataSourceID="CounterSource" Runat="Server"> <ItemTemplate> Visitors: <asp:Label Font-Bold="True" Runat="Server" Text='<%# String.Format("{0:N0}", Eval("VisitorCounter")) %>'/> </ItemTemplate> </asp:FormView> </td> </tr> <tr> <td id="MenuCell" nowrap> <!-- Menu --> <asp:Label Text="Menu" Runat="Server" BackColor="#990000" ForeColor="#FFFFFF" Font-Bold="True" Width="130px" Style="padding:2px"/><br/> <asp:SiteMapDataSource id="SiteMapSource" Runat="Server" SiteMapProvider="eCommerceProvider" ShowStartingNode="False"/> <asp:Menu Runat="Server" DataSourceID="SiteMapSource" Width="130" StaticMenuItemStyle-ForeColor="#990000" StaticHoverStyle-BackColor="#FFFFFF"/><br/> <!-- Category Search --> <asp:Label Text="Category" Runat="Server" BackColor="#990000" ForeColor="#FFFFFF" Font-Bold="True" Width="130px" Style="padding:2px"/><br/> <asp:AccessDataSource id="BookSource" Runat="Server" DataFile="../Databases/BooksDB.mdb" SelectCommand="SELECT DISTINCT BookType FROM Books ORDER BY BookType"/> <asp:GridView DataSourceID="BookSource" Runat="Server" AutoGenerateColumns="False" ShowHeader="False" GridLines="Both" Width="130"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:Panel Width="125" Runat="Server" onMouseOver="this.style.backgroundColor='#FFFFFF'" onMouseOut="this.style.backgroundColor='#E0E0E0'"> <asp:LinkButton Text='<%# Eval("BookType") %>' Runat="Server" OnCommand="Get_Category" CommandName='<%# Eval("BookType") %>' ForeColor="#990000" Font-Underline="False"/> </asp:Panel> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> <br/> <!-- Criterion Search --> <asp:Label Text="Search" Runat="Server" BackColor="#990000" ForeColor="#FFFFFF" Font-Bold="True" Width="130px" Style="padding:2px"/><br/> <asp:TextBox id="SearchCriterion" Runat="Server" Font-Size="8pt" Width="105px"/> <asp:Button Text="Go" OnClick="Get_Criterion" Runat="Server" Font-Size="8pt"/> </td> <td id="ContentCell"> <asp:ContentPlaceholder id="CONTENT" Runat="Server"/> </td> </tr> </table> </form> </body> </html>
A short external style sheet is linked to the master page to set overall styling for the page and its structuring table. Code for this stylesheetEC.css file is shown in Listing 13-2. All other styling is applied with server style properties assigned to individual server controls.
body {margin:0px; background-image:url(Backdrop.gif); background-repeat:repeat-y} table#MasterTable td, th {font-family:arial; font-size:10pt; vertical-align:top} table#MasterTable td#TitleCell {background-color:#990000; color:white; padding:5px; font-size:32pt; font-weight:bold} table#MasterTable td#CounterCell {background-color:#E0E0E0} table#MasterTable td#MenuCell {width:150px; padding:5px} table#MasterTable td#ContentCell {width:100%; padding:10px}
Coding the Site Menu
The site menu has direct links to three pages: the home page (Default.aspx), the shopping cart page (ShopCart.aspx), and a page of special-offer books (Specials.aspx). The menu is produced from a web.sitemap file in the eCommerce root directory. Code for this file is shown in Listing 13-3. Links are relative to the eCommerce directory where all site pages are stored.
<?xml version="1.0" ?> <siteMap> <siteMapNode> <siteMapNode title="Home" url="Default.aspx"/> <siteMapNode title="Shopping Cart" url="ShopCart.aspx"/> <siteMapNode title="Specials" url="Specials.aspx"/> </siteMapNode> </siteMap>
Placement of the site menu on the master page uses a SiteMapDataSource control to point to the default web.sitemap file as the source of menu items. The starting, root node (<siteMap>) is not part of the menu display. A Menu control displays these items by linking to the SiteMapDataSource through its DataSourceID property.
<asp:SiteMapDataSource id="SiteMapSource" Runat="Server" ShowStartingNode="False"/> <asp:Menu id="SiteMenu" Runat="Server" DataSourceID="SiteMapSource" Width="130" StaticMenuItemStyle-ForeColor="#990000" StaticHoverStyle-BackColor="#FFFFFF"/><br/>
Coding the Category Search
One of the site search options is to view all books by category, that is, by their BookType values in the database. These categories are drawn from the database and displayed in a series of LinkButton controls arranged inside a GridView as shown in Listing 13-5.
<asp:AccessDataSource id="BookSource" Runat="Server" DataFile="../Databases/BooksDB.mdb" SelectCommand="SELECT DISTINCT BookType FROM Books ORDER BY BookType"/> <asp:GridView DataSourceID="BookSource" Runat="Server" AutoGenerateColumns="False" ShowHeader="False" GridLines="Both" Width="130"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:Panel Width="125" Runat="Server" onMouseOver="this.style.backgroundColor='#FFFFFF'" onMouseOut="this.style.backgroundColor='#E0E0E0'"> <asp:LinkButton Text='<%# Eval("BookType") %>' Runat="Server" OnCommand="Get_Category" CommandName='<%# Eval("BookType") %>' ForeColor="#990000" Font-Underline="False"/> </asp:Panel> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
An AccessDataSource retrieves all distinct BookTypes from the database for use as text labels and command names for the LinkButtons. Clicking a button calls subprogram Get_Category to transfer to the Search.aspx page, appending the chosen category type passed to the subprogram through the button's command name.
<SCRIPT Runat="Server"> Sub Get_Category (Src As Object, Args As CommandEventArgs) Response.Redirect("Search.aspx?Category=" & Args.CommandName) End Sub ... </SCRIPT>
The Get_Category subprogram adds the passed command name (Args.CommandName)which is a BookType valueto a query string and redirects to the Search.aspx page. This query string provides notification to the page of the category-type listing of books to produce.
Notice that each LinkButton is surrounded by a Panel control with two JavaScript event handlers: onMouseOver and onMouseOut. These events trigger the enclosed JavaScripts to change the background color of the Panels to white and back to gray as the mouse moves on and off the Panels. This code causes the LinkButtons to behave visually like the site menu which uses similar hover styles.
Coding the Criterion Search
A second form of book search is provided. In this case, the visitor can enter a word or phrase (full or partial) into a textbox and perform a search of the database for a matching value. As in the case with a category search, the entered search criterion value is passed to the Search.aspx page to perform the lookup. Code for the Get_Criterion subprogram to redirect to the search page is shown in Listing 13-7.
Sub Get_Criterion (Src As Object, Args As EventArgs) If SearchCriterion.Text <> "" Then Response.Redirect("Search.aspx?Criterion=" & SearchCriterion.Text) End If End Sub
Here, the search value is appended as a query string to the URL for the Search.aspx page. It is taken directly from the SearchCriterion TextBox control.
Keeping Track of Customers
There needs to be a way of keeping track of visitors to the site. In addition to being window shoppers, visitors can become customers and select books for purchase. Therefore, different purchases made by different customers need to be distinguished. One way of doing this is to assign a unique identification number to each person who arrives at the site. A random number for this purpose is assigned in the Page_Load subprogram coded at the top of the eCommerce.master page.
Sub Page_Load If Session("OrderNumber") = "" Then RANDOMIZE Session("OrderNumber") = (INT((9999999 - 1111111 + 1) _ * RND + 1111111)).toString() End If End Sub
This random number formula produces a seven-digit number between 1111111 and 9999999. This number is assigned to a Session variableSession("OrderNumber")to remain with the visitor while navigating the site. If the visitor leaves the site or halts navigation for longer than 20 minutes, or the visitor closes the browser window before returning, a new number is assigned.
It is important to keep in mind that for each visitor to a Web site ASP.NET automatically creates a Session for that individual. A special Session ID is assigned so that the activities of concurrent visitors can be individually managed by ASP.NET. Session variable OrderNumber plays a similar role for the eCommerce site. It is a way of keeping track of concurrent customers and, as discussed later for the shopping cart, of keeping track of which purchases go with which customers.
Since the order number generator routine is on the eCommerce.master page, it can produce a new number each time a page is loaded. However, this number needs to be created only one time, the first time the visitor arrives at the site. Therefore, a condition test permits creation of this number only if it does not yet exist.
Even with generation of a seven-digit random number, there is the possibility, however slight, that the same number can be produced for different individuals. Although this possibility is not checked in the example site, the safest bet is to also write this number to a database. Then, the number-generator routine can check against this database for duplicate numbers being produced, and generate another number if this is the case. This precautionary step is left for your discretion and programming.
Counting Visitors - The global.asax File
A visitor counter is a popular device for Web sites and is included on the eCommerce site. A visitor counter keeps track of the number of different visitors to a site. It does not add to the tally for each page visited; it tracks the number of visitors who newly arrive at the site irrespective of how long they stay, how many pages they view, or whether they temporarily leave and return.
The concept of a Session fits in well with this definition of a visitor. An ASP.NET Session is automatically created when a visitor first arrives at any page of a site; it remains in effect as long as the visitor is active within a 20-minute time interval or until the visitor closes the browser window. It remains active even if visitors leave the site, as long as they return within the 20-minute time period.
Given that a Session is a practical definition of a visit, a counter can be incremented each time ASP.NET creates a new Session as a means of counting visitors. It happens that ASP.NET provides the perfect place to increment this counterin the global.asax file. Note that the file extension is .asax, not .aspx.
A global.asax file is located in the root Web directory and is a scripted page that runs each time a new Session is created. It does not contain any pre-scripted routines and, in fact, the file does not exist until you create it. You create the file only if you have scripts to run during Session creation. The following global.asax file appears in the root eCommerce directory. It contains a script to increment a visitor counter for the site.
<%@ Import Namespace="System.Data.OleDb" %> Sub Session_Start '-- Increment visitor counter Dim DBConnection As OleDbConnection = New OleDbConnection( _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & Server.MapPath("Databases/BooksDB.mdb")) DBConnection.Open() Dim SQLString As String SQLString = "UPDATE Counters SET VisitorCounter = VisitorCounter + 1" Dim DBCommand As OleDbCommand = New OleDbCommand(SQLString, DBConnection) DBCommand.ExecuteNonQuery() DBConnection.Close() End Sub
The Session_Start subprogram is recognized by ASP.NET and is run automatically whenever a new Session is created for a newly arrived visitor at a site. In this case, the subprogram updates the VisitorCounter field in the Counters table of the BooksDB.mdb database. This table is added to the database along side the Books table that has been used in previous examples in these tutorials. Its layout is shown below.
The single VisitorCounter field contains a single record defined as a double integer type. You need to initialize the counter to 0 at the beginning of the counting process. Of course, if you wish to impress visitors, set the counter to a much larger number.
A report of the VisitorCounter appears at the top of the master page. A link to the field is made through an AccessDataSource bound to a FormView that displays the visitor count. Code for these controls is repeated in Listing 13-10.
<asp:AccessDataSource id="CounterSource" Runat="Server" DataFile="../Databases/BooksDB.mdb" SelectCommand="SELECT VisitorCounter FROM Counters"/> <asp:FormView id="VisitorCount" DataSourceID="CounterSource" Runat="Server"> <ItemTemplate> Visitors: <asp:Label Font-Bold="True" Runat="Server" Text='<%# String.Format("{0:N0}", Eval("VisitorCounter")) %>'/> </ItemTemplate> </asp:FormView>
Although only a single data value is displayed, it must be rendered through a FormView or other type of control that permits data binding. A stand-alone Label control, for instance, cannot be bound to an AccessDataSource. A FormView is used because it permits selective display of data values in its <ItemTemplate>.
Content Pages
The initial content page for the sitethe "home" page is Default.aspx; it is retrieved when linking to the site itself. This page includes little information in the example, and other information should be added to make it more informative and inviting. Its layout as a content page is shown in Listing 13-11.
<%@ Page MasterPageFile="eCommerce.master" Language="vb" Debug="True"%> <asp:Content id="Home" ContentPlaceHolderID="CONTENT"> Runat="Server" <p>Welcome to the <asp:Label Text="webWarehouse" Runat="Server" Font-Size="14pt" ForeColor="#990000"/>, your source for computer- and Web-related books.</p> <p>Click any of the Categories to view available books of that type, or enter a value into the Search box to locate book titles, descriptions, or author names containing that entered text.</p> </asp:Content>
The ContentPlaceHolderID="CONTENT" property of the Content control points to the like-named ContentPlaceHolder control on the master page where this page's content appears. As you can see, all that needs to be coded on a content page is whatever information appears inside the master page placeholder.