1. Copy the ProductImages folder from the Source Code area on the Apress web site to your BalloonShop solution.
2. Add a new Web User Control named ProductsList to the UserControls folder.
3. While in Source View, add the following code that will generate the “Page x of y … Previous … Next”
text in sections of products that have more pages.
<asp:Label ID="pagingLabel" Runat="server"
CssClass="PagingText" Visible="false" />
<asp:HyperLink ID="previousLink" Runat="server"
CssClass="PagingText" Visible="false">Previous</asp:HyperLink>
<asp:HyperLink ID="nextLink" Runat="server"
CssClass="PagingText" Visible="false">Next</asp:HyperLink>
4. Open the control in Design View, and drag a DataList control from the toolbox to the bottom of the control.
5. Rename the ID of the DataList to list, and set its RepeatColumns property to 2 (specifies the number of products to be displayed per row), and RepeatDirection to Horizontal.
6. Edit the DataList’s code directly in Source View:
<asp:DataList ID="list" Runat="server" RepeatColumns="2">
<ItemTemplate>
<table cellPadding="0" align="left">
<tr height="105">
<td align="center" width="110">
<a href='Product.aspx?ProductID=<%# Eval("ProductID")%>'>
<img width="100" src='ProductImages/
<%# Eval("Image1FileName") %>' border="0"/>
</a>
</td>
<td vAlign="top" width="250">
<a class="ProductName" href='Product.aspx?ProductID=
<%# Eval("ProductID")%>'>
<%# Eval("Name") %>
</a>
<br/>
<span class="ProductDescription">
<%# Eval("Description") %>
<br/><br/>
Price:
</span>
<span class="ProductPrice">
<%# Eval("Price", "{0:c}") %>
</span>
</td>
</tr>
</table>
</ItemTemplate>
</asp:DataList>
7. Add these styles to BalloonShop.css:
a.ProductName {
color: Red;
font-family: 'Trebuchet MS';
text-decoration: none;
font-weight: bold;
font-size: 12px;
}
a.ProductName:hover {
text-decoration: underline;
}
.ProductDescription {
color: Black;
font-family: Verdana, Helvetica, sans-serif;
font-size: 11px;
}
.ProductPrice {
color: Black;
font-family: Verdana, Helvetica, sans-serif;
font-weight: bold;
font-size: 11px;
}
.PagingText {
font-family: Verdana, Helvetica, sans-serif;
font-size: 11px;
color: Black;
}
8. The way ASP.NET outputs product prices to the visitor depends on the culture settings of the computer running the site. You told ASP.NET which numbers represent prices by using the {0:C} formatting parameter in the Eval expression. For example, if the default culture is set to fr-FR, instead of $1.23 you would see 1,23EUR. (For more information on internationalization issues, consult an advanced ASP.NET book.) For now, to make sure the prices are expressed in the same currency (U.S. dollars for this example), double-click web.config in Solution Explorer and add the <globalization> element under <system.web>, like this:
...
<globalization requestEncoding="utf-8" responseEncoding="utf-8"
culture="en-US"/>
</system.web>
</configuration>
This ensures that no matter how the development (or production) machine is set up, your prices will always be expressed in the same currency.
9. Modify ProductsList class in ProductsList.ascx.cs like this:
using System.Collections.Specialized;
public partial class ProductsList : System.Web.UI.UserControl {
protected void Page_Load(object sender, EventArgs e) {
PopulateControls();
}
private void PopulateControls() {
// Retrieve DepartmentID from the query string
string departmentId = Request.QueryString["DepartmentID"];
// Retrieve CategoryID from the query string
string categoryId = Request.QueryString["CategoryID"];
// Retrieve Page from the query string string page = Request.QueryString["Page"];
if (page == null) page = "1";
// How many pages of products?
int howManyPages = 1;
// If browsing a category...
if (categoryId != null) {
// Retrieve list of products in a category list.DataSource =
CatalogAccess.GetProductsInCategory(categoryId, page, out howManyPages);
list.DataBind();
}
else if (departmentId != null) {
// Retrieve list of products on department promotion
list.DataSource = CatalogAccess.GetProductsOnDepartmentPromotion (departmentId, page, out howManyPages);
list.DataBind();
} else {
// Retrieve list of products on catalog promotion list.DataSource =
CatalogAccess.GetProductsOnCatalogPromotion(page, out howManyPages);
list.DataBind();
}
8213592a117456a340854d18cee57603
// display paging controls if (howManyPages > 1) {
// have the current page as integer int currentPage = Int32.Parse(page);
// make controls visible pagingLabel.Visible = true;
previousLink.Visible = true;
nextLink.Visible = true;
// set the paging text
pagingLabel.Text = "Page " + page + " of " + howManyPages.ToString();
// create the Previous link if (currentPage == 1)
previousLink.Enabled = false;
else {
NameValueCollection query = Request.QueryString;
string paramName, newQueryString = "?";
for (int i = 0; i < query.Count; i++) if (query.AllKeys[i] != null)
if ((paramName = query.AllKeys[i].ToString()).ToUpper() != "PAGE") newQueryString += paramName + "=" + query[i] + "&";
previousLink.NavigateUrl = Request.Url.AbsolutePath + newQueryString + "Page=" + (currentPage - 1).ToString();
}
// create the Next link
if (currentPage == howManyPages) nextLink.Enabled = false;
else {
NameValueCollection query = Request.QueryString;
string paramName, newQueryString = "?";
for (int i = 0; i < query.Count; i++) if (query.AllKeys[i] != null)
if ((paramName = query.AllKeys[i].ToString()).ToUpper() != "PAGE") newQueryString += paramName + "=" + query[i] + "&";
nextLink.NavigateUrl = Request.Url.AbsolutePath + newQueryString +
"Page=" + (currentPage + 1).ToString();
} } } }
10. Open Catalog.aspx in Design View. Drag ProductsList.ascx from Solution Explorer, drop it near the [Place List of Products Here] text, and then delete the text, as shown in Figure 4-20.
Figure 4-20. Adding ProductsList.ascx to Catalog.aspx
11. Now do the same in Default.aspx.
12. Press F5 to execute the project. The main page should now be populated with its featured products (see Figure 4-21).
■ Note To see the product images, make sure you have the ProductImages folder available (located in your project’s folder) from the Source Code area on the Apress web site (http://www.apress.com).
Figure 4-21. The front page of BalloonShop
13. Now click a department to see the department’s featured products, and then click a category to see all the products in that category. Figure 4-22 shows the paging feature in action.
Figure 4-22. Paging in action
How It Works: The ProductsList Web User Control
ProductsList.ascx, just like CategoriesList.ascx and DepartmentsList.ascx, uses a DataList to paint a list of items. Because ProductsList.ascx will be reused in both Default.aspx and Catalog.aspx, you can’t know beforehand exactly what it needs to display. Is it the list of products on catalog promotion, or is it the list of products in a particular category? In the usual style, ProductsList decides what it needs to display by ana- lyzing the query string parameters. The logic is simple, so we won’t go through it again here.
The first two new issues you played with in this exercise are the globalization setting and the paging controls. Setting the application to use the en-US culture ensures that monetary data (product prices) will always be displayed using the dollar symbol, no matter the settings of the computer that hosts the application.
Paging works through a query string parameter named Page. When a list of products is to be displayed and Page doesn’t show up in the query string, the code automatically assumes the visitor is on the first page. For example, both these links would forward the visitor to the first page of products for the main page of the catalog:
http://localhost/BalloonShop/Catalog.aspx?DepartmentID=1 http://localhost/BalloonShop/Catalog.aspx?DepartmentID=1&Page=1
The Previous and Next links have the simple role of incrementing or decrementing the value of the Page query string parameter. Note that the paging controls only appear if there’s more than one subpage of products. You find out this detail from the business tier GetProducts... functions, which return the total number of subpages through the howManyPages out parameter:
// If browsing a category...
if (categoryId != null) {
// Retrieve list of products in a category list.DataSource =
CatalogAccess.GetProductsInCategory(categoryId, page, out howManyPages);
list.DataBind();
}
You make the paging controls visible only if the number of subpages is greater than 1:
// display paging controls if (howManyPages > 1)
When making the paging controls visible, the main challenge is to build the links for Previous and Next. For example, the Next link should be the same as the currently loaded page, except the Page value in the query string should incremented by one, but it shouldn’t be enabled if the visitor is on the last page. You do this by browsing through the collection of query string parameters and reconstructing the complete query string:
// create the Next link
if (currentPage == howManyPages) nextLink.Enabled = false;
else {
NameValueCollection query = Request.QueryString;
string paramName, newQueryString = "?";
for (int i = 0; i < query.Count; i++) if (query.AllKeys[i] != null)
if ((paramName = query.AllKeys[i].ToString()).ToUpper() != "PAGE") newQueryString += paramName + "=" + query[i] + "&";
nextLink.NavigateUrl = Request.Url.AbsolutePath + newQueryString + "Page=" + (currentPage + 1).ToString();
}
The logic that builds the Previous link is similar to the code for the Next link.