Order-Related Modules for an ASP.NET AJAX Server-Centric Based Online Shopping Website

Welcome to the fifth part of an eleven-part series that focuses on helping you build your own online shopping web site. Things up to now have become more and more interesting because we are about to delve into the following modules: viewing products, going shopping, placing orders, and so forth. Let’s discuss them one by one.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 7
December 26, 2007
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

A downloadable .rar file is available for this article.

Viewing Products

Viewing products is mainly accomplished through the "Product.aspx" page. Let's first take a quick look at the most complicated page (in fact the main page). The following Figure 15 shows one of the run-time snapshots when no users have been registered.


Figure 15-one of the run-time snapshots for the "Product.aspx" page 

On the whole, this page is a typical four-part user interface design mainly based on HTML <table> elements. The topmost part is the ugly logo by me. The left part (which is composed of several user controls, and because no users have been registered, parts of other sub panels do not appear) acts as the control panel to schedule each component of the whole shopping city. The right and larger part consists of two parts. The upper part is a product category list (DataList), and the lower part shows the corresponding products that belong to the selected product category (GridView). The bottom part of this page is a simple footnote just to simulate a typical website layout.

Behind the Scenes

Behind the Scenes

Now let's explore the HTML coding. Here, again, due to the length of the contents we only list the most important part, as follows.

<td align="center" style="width: 577px;" valign="top">

<asp:UpdatePanel ID="UpdatePanel1" runat="server">

<Triggers>

<asp:AsyncPostBackTrigger ControlID="ProductList" EventName="ItemCommand" />

</Triggers>

<ContentTemplate>

<asp:GridView ID="ProductView" CssClass="Text" runat="server" Width="100%" AutoGenerateColumns="False" DataKeyNames="ProductID" ShowHeader="False" OnRowCommand="ProductView_RowCommand" OnRowCreated="ProductView_RowCreated">

<FooterStyle ForeColor="White" BackColor="#3B6BD1" Font-Bold="True"></FooterStyle>

<SelectedRowStyle Font-Bold="True" ForeColor="Navy" BackColor="#FFCC66" BorderColor="CornflowerBlue" />

<RowStyle ForeColor="#333333" BackColor="#FFFBD6" BorderColor="CornflowerBlue" BorderStyle="Solid" BorderWidth="1px" />

<Columns>

<asp:TemplateField>

<ItemTemplate>

<table cellpadding="2" cellspacing="0" width="100%" border="0" class="Text">

<tr>

<td>

<a href='../Admin/Product/ProductInfo.aspx?ProductID=<%# DataBinder.Eval(Container.DataItem,"ProductID")%>' target="_blank">

<asp:Panel ID="Panel1" runat="server" Height="50px" Width="125px">

<asp:Image ID="ProductPicture" Runat="server" Width="90" Height="120" ImageUrl='<%# Eval("PictureID", "../Handler.ashx?Id={0}") %>' > </asp:Image>

 

</asp:Panel></a>

</td>

<td style="width: 100%;" valign="top">

<table cellpadding="0" cellspacing="0" border="0" class="Text" style="width: 100%;">

<tr><td style="width: 50%;" align="left" valign="top">

<table cellpadding="0" style="width: 100%;" cellspacing="0" border="0">

<tr>

<td><strong class="Title">Product Name:</strong><a href='../Admin/Product/ProductInfo.aspx?ProductID=<%# DataBinder.Eval(Container.DataItem,"ProductID")%>' target="_blank"><%# DataBinder.Eval(Container.DataItem,"Name")%></a>

</td>

</tr>

<tr>

<td><strong class="Title">Price:</strong><asp:Label ID="Price" runat="server" Text='<%# DataBinder.Eval(Container.DataItem,"OutPrice")%>'></asp:Label>

</td>

</tr>

</table>

</td>

<td style="width: 50%;" align="left" valign="top">

<table cellpadding="0" style="width: 100%;" cellspacing="0" border="0" class="Normal">

<tr>

<td><strong class="Title">Manufacturer:</strong><%# DataBinder.Eval(Container.DataItem,"Sell")%></td>

</tr>

<tr>

<td><strong class="Title">Manufacturing Date:</strong><%# DataBinder.Eval(Container.DataItem,"CreateDate")%></td>

</tr>

</table>

</td>

</tr>

<tr><td colspan="2" align="left"><strong class="Title">Description:</strong><br /><%# DataBinder.Eval(Container.DataItem,"Desn")%></td></tr>

<tr><td colspan="2" align="left"><hr size="1" /></td></tr>

<tr><td colspan="2" align="left"><a href='../Admin/Product/ProductInfo.aspx?ProductID=<%# DataBinder.Eval(Container.DataItem,"ProductID")%>' target="_blank">

Details</a>&nbsp;&nbsp;&nbsp;<a href='../Desktop/Comment.aspx?ProductID=<%# DataBinder.Eval(Container.DataItem,"ProductID")%>' target="_blank">

View Product Comment</a>

 

<asp:Button ID="BuyBtn" Runat="server" CssClass="ButtonCss" Text="Add to shopping cart" Width="50%" CommandName='<%#DataBinder.Eval(Container.DataItem,"Name") %>'></asp:Button>

</td></tr>

</table>

</td>

</tr>

</table>

</ItemTemplate>

</asp:TemplateField>

</Columns>

</asp:GridView>

</ContentTemplate>

</asp:UpdatePanel>

</td>

First, the above code only relates to the GridView control that is used to display the corresponding product information that belongs to the selected product category. To gain a partially refreshing effect, we typically use an ASP.NET AJAX server control named UpdatePanelto enclose the GridView control.

More Behind the Scenes Code

Second, we've leveraged one of the important triggers, AsyncPostBackTrigger,to invoke the updating action of the UpdatePanelcontrol. The following shows the related code:

private void BindProductData(int nCategoryID){

///define the class that gets the data

Product product = new Product();

SqlDataReader dr = product.GetProductByCategory(nCategoryID);

///Set the control's data source

ProductView.DataSource = dr;

///bind data to the control

ProductView.DataBind();

///Close the database connection

dr.Close();

}

......

protected void ProductList_ItemCommand(object source, DataListCommandEventArgs e){

BindProductData(Int32.Parse(e.CommandArgument.ToString()));

}

Here we have already provided detailed comments. As you see, the general routine of the code is like those arranged in the previous modules. Here, the "ProductView" control corresponds to the ASP.NET GridView to display the selected goods, while the "ProductList" refers to the ASP.NET control named DataList to show the product category.

For now, acute readers may have already detected a secret. Here, again, we list the related code:

<a href='../Admin/Product/ProductInfo.aspx?ProductID=<%# DataBinder.Eval(Container.DataItem,"ProductID")%>' target="_blank">

<asp:Panel ID="Panel1" runat="server" Height="50px" Width="125px">

<asp:Image ID="ProductPicture" Runat="server" Width="90" Height="120" ImageUrl='<%# Eval("PictureID", "../Handler.ashx?Id={0}") %>' > </asp:Image>

</asp:Panel></a>


How can we display the images within the database? As the previous sections mentioned, we use a field of the "image" type to directly store the binary image data into the table. In earlier ASP.NET solutions, it is very difficult to show an image at the specified position on a web page, especially as the image data is persisted into the database table. Another commonly-used way to deal with image data is to independently store the image files while only their related paths are in the table. Each solution has its own pros and cons.

Web Handler

 

Here, we adopt the new ASP.NET 2.0 project-the newly-introduced web handler ".ashx" file. According to MSDN, the web handler ".ashx" file works just like an aspx file except WE are one step away from the messy browser level where HTML and C# mix. One reason we would write an .ashx file instead of an .aspx file is that our output is not going to a browser but to an XML-consuming client of some kind. As you have seen, the key piece lies in 'ImageUrl='<%# Eval("PictureID", "../Handler.ashx?Id={0}") %>''. And the following corresponds to the final form of it detected in the browser side.


<img id="ProductView_ctl02_ProductPicture" style="border-width: 0px;
height: 120px; width: 90px;" src="../Handler.ashx?Id=5"/>

Now, let's take a further look into the code behind "Handler.ashx." Since the total code is short, without further ado, we list all of it:

<%@ WebHandler Language="C#" Class="Handler" %>

//using namespaces (omitted)

public class Handler : IHttpHandler {

public void ProcessRequest(HttpContext context){

//context.Response.ContentType = "image/jpeg";

context.Response.Cache.SetCacheability(HttpCacheability.Public);

context.Response.BufferOutput = false;

Stream outstream = null;

int photoId = -1;

if (context.Request.QueryString["Id"] != null &&
context.Request.QueryString["Id"] != "")

{

photoId = Convert.ToInt32(context.Request.QueryString["Id"]);

outstream = GetPhoto(photoId);

}

const int buffersize = 1024 * 16;

byte[] buffer = new byte[buffersize];

int count = outstream.Read(buffer, 0, buffersize);

while (count > 0) {

context.Response.OutputStream.Write(buffer, 0, count);

count = outstream.Read(buffer, 0, buffersize);

}

}

public Stream GetPhoto(int photoId) {

SqlConnection myConnection = new SqlConnection
(ConfigurationManager.ConnectionStrings
["SQLCONNECTIONSTRING"].ConnectionString);

SqlCommand myCommand = new SqlCommand

("SELECT [Data] FROM [Pictures] WHERE [PictureID]=@PictureID",
myConnection);

myCommand.CommandType = CommandType.Text;

myCommand.Parameters.Add(new SqlParameter("@PictureID", photoId));

myConnection.Open();

object result = myCommand.ExecuteScalar();

try{

return new MemoryStream((byte[])result);

}

catch (ArgumentNullException e) {

return null;

}

finally{

myConnection.Close();

}

}

public bool IsReusable {

get { return false; }

}

}

In the above code, the core component is the public method, namelyProcessRequest. When the parameterized URL is passed to it, it accepts the parameter to a variable named "photoId," as follows.

photoId = Convert.ToInt32(context.Request.QueryString["Id"]);

Next, in the helper function GetPhoto, I directly use SQL operations to fetch the image data from the database (of course you can move the module elsewhere if you prefer). The subsequent lines of code in the ProcessRequest methodabstracts the binary image data and then writes it to the specified context, as follows:

while (count > 0) {

context.Response.OutputStream.Write(buffer, 0, count);

count = outstream.Read(buffer, 0, buffersize);

}

The reason that we leveraged a whileloop is just to more efficiently deal with the data-you can follow your own calculating algorithms.


As for the "product.aspx.cs" background file, there is nothing more particular to notice but the common data source binding operations, as follows.

ProductView.DataSource = dr;

ProductView.DataBind();

So, you see, the .jpg (or .png, .gif) files stored in the database are finally displayed at the specified position on the web page!

Author's Note: Here you should be careful to refer to the path of the ".ashx" files, or else you won't be able to see the image.

Buying Goods

After clicking the "Add to shopping cart" button, the related item can be put into the shopping cart, all of which is finished within the ProductView_RowCommand click event handler. In conclusion, the function will accomplish the following tasks:

  1. If there are no products inside the shopping cart, create an instance of the  OrderInfo(i.e. order) class which is used to save the cart related info;
  2. Obtain information about the product and create an instance of the OrderItemInfo(i.e. item) class to save this product-related info;
  3. Add the item objectinto the OrderItemList listof the order object;
  4. Update the info of the cart;
  5. If there are already products inside the shopping cart, get the order object used to save the cart-related info from the Session system variable;
  6. Obtain info of the product and create an instance of the OrderItemInfo(i.e. item) class to save this product-related info;
  7. Search from inside the order objectsub items that contain the same product as that inside the item project, and update the sub items info;
  8. Update the shopping cart.


The following lists the complete code for the ProductView_RowCommand function:

protected void ProductView_RowCommand(object
sender,GridViewCommandEventArgs e){

OrderItemInfo item = null;

OrderInfo order = null;

if(Session[Session.SessionID + OrderForm.Cart] == null) {

item = GetOrderItemInformation(e);

if(item == null) {

ScriptManager.RegisterStartupScript(Page, GetType(), "script1",

"alert('Data Error!');", true);

return;

}

order = new OrderInfo();

order.OrderItemList.Add(item);

order.TotalMoney = item.Price;

order.TotalNumber = item.Number;

Session[Session.SessionID + OrderForm.Cart] = order;

}

else{

order = (OrderInfo)Session[Session.SessionID + OrderForm.Cart];

item = GetOrderItemInformation(e);

if(item == null) {

ScriptManager.RegisterStartupScript(Page, GetType(), "script2",

"alert('Data Error!');", true);

return;

}

int i = 0;

for(i = 0; i < order.OrderItemList.Count; i++){

if(item.ProductID == ((OrderItemInfo)order.OrderItemList[i]).ProductID) {

((OrderItemInfo)order.OrderItemList[i]).Number++;

((OrderItemInfo)order.OrderItemList[i]).ItemTotalMoney +=
item.ItemTotalMoney;

break;

}

}

if(i == order.OrderItemList.Count) {

order.OrderItemList.Add(item);

}

order.TotalNumber++;

order.TotalMoney += item.Price;

Session[Session.SessionID + OrderForm.Cart] = order;

}

ScriptManager.RegisterStartupScript(Page, GetType(), "script3",

"alert('You have succeeded in adding the selected goods into the shopping
cart!');", true);

}

For brevity, we omit the code listing for the GetOrderItemInformation helper function.

In addition, there are several issues that deserve to be discussed. First, because when we get the product information, the row index of the ProductView controlhas to be used, at the earlier event (RowCreated) of the ProductView controlwe put the index number of the product into the CommandArgument propertyof the buyBtn button(which is the IDproperty of the "Add to shopping cart" button). Here's the related code:

protected void ProductView_RowCreated(object sender,GridViewRowEventArgs
e){

if(e.Row.RowType == DataControlRowType.DataRow) {

Button buyBtn = (Button)e.Row.FindControl("BuyBtn");

if(buyBtn != null) {

buyBtn.CommandArgument = e.Row.RowIndex.ToString();

}

}

}

Second, in traditional ASP.NET applications most of us like to use 'Response.Write("<script>window.alert('....')</script>");' to tell users the result of the current operation. However, due to leveraging the ASP.NET AJAX server control named UpdatePanelto enclose the GridView control we can not use that any more, or else we will meet some error like the one displayed in Figure 16.


Figure 16-an error occurs when we use 'Response.Write(...);' inside UpdatePanel control to give some clue to the users

Therefore, we have to resort to a static function named RegisterStartupScriptof the ScriptManager control. Here is an example of what happens when the "adding" operation is successful.

ScriptManager.RegisterStartupScript(Page, GetType(), "script3",

"alert('You have succeeded in adding the selected goods into the shopping
cart!');", true);

Viewing the Shopping Cart

This is a must have for the customers that have already logged into the system. When they click the "My Shopping Cart" hyperlink on the left of the "product.aspx" page they will be redirected to another page, "cart.aspx" whose snapshot is shown in the following Figure 17.


Figure 17-the design-time snapshot for viewing the shopping cart

Here, you can click the "Product Name" related hyperlink to view the detailed information about the selected goods or delete it by click the "X" symbol (a hyperlink) at the rightmost of the row. Since this is a simple ASP.NET page we won't discuss it any more. But there is a flaw that needs to be pulled: here although in the "cart.aspx" page the customer can change the count of the specified article, but when he is navigated to another page, "order.aspx." to finally place the order, he still cannot change the number of that article. So, sorry for my not having provided this support; you have to click "Add to the shopping cart" button ntimes if you want to buy nproducts of the related one.

Committing the order

As hinted above, when the user clicks the button labeled "Submit this shopping and create the order" on the "cart.aspx" page he will be navigated to the  "order.aspx" page to finally commit the order. Now let's first take a look at one of the run-time snapshot as illustrated in Figure 18.


Figure 18-the run-time snapshot for submitting the final order

As seen from the figure, the GridView control at the top gives detailed info about each item for purchase. Neighboring it there is a line further totaling all the info above it. The large part at the middle of the page display is the contact information of the current customer. Clicking the "Previous" button will lead the user back to the "View shopping cart" page, while clicking the "Confirm the shopping info and commit the order" button means the user has confirmed all the above info and decided to submit the order to the seller.

Page Initialization

At the start of the initialization of the "order.aspx," related data are bound to the ProductView control, i.e. displaying the products within the cart, then continue to show the detailed info about the user and the order. The following code corresponds to the initializing process.

protected void Page_Load(object sender,EventArgs e){

if(!Page.IsPostBack){

///get the info about the cart

ShowCartInfo();

if(Session["UserID"]!=null)

BindUserData(Int32.Parse(Session["UserID"].ToString()));

}

}

private void BindUserData(int nUserID){

///Get the data

User user = new User();

SqlDataReader recr = user.GetSingleUser(nUserID);

///Read the data

if(recr.Read()){

///display data

UserName.Text = recr["UserName"].ToString();

RealName.Text = recr["RealName"].ToString();

Address.Text = recr["Address"].ToString();

Email.Text = recr["Email"].ToString();

Phone.Text = recr["Phone"].ToString();

Mobile.Text = recr["Mobile"].ToString();

Remark.Text = recr["Remark"].ToString();

}

recr.Close();///Close the data source

}

private void ShowCartInfo(){

///judge if there are data in the cart

if(Session[Session.SessionID + OrderForm.Cart] == null) {

return;

}

OrderInfo order = (OrderInfo)Session[Session.SessionID + OrderForm.Cart];

ProductView.DataSource = order.OrderItemList;

ProductView.DataBind();

ProductView.FooterRow.Cells[0].Text = "Total amount: " +
order.TotalNumber.ToString() + ", Total price: " +
order.TotalMoney.ToString();

}

Notwithstanding the fact that it is pretty long, the inner logic is rather easy. The key sentence is 'SqlDataReader recr = user.GetSingleUser(nUserID);' while the key variable still lies in Session. OK, by calling the two helper functions, namedShowCartInfoand BindUserData, all the required info is displayed on the screen.

Submitting the Order

The following shows the click event handler for the "Confirm the shopping info and commit the order" in the above Figure 18.

protected void CommitBtn_Click(object sender,EventArgs e){

///judge if there are data in the cart

if(Session[Session.SessionID + OrderForm.Cart] == null) {

return;

}

OrderInfo order = (OrderInfo)Session[Session.SessionID + OrderForm.Cart];

OrderForm orderform = new OrderForm();

int nOrderFormID = orderform.AddOrderForm(Int32.Parse(Session
["UserID"].ToString()),

order.TotalNumber,

order.TotalMoney);

if(nOrderFormID > -1) {

OrderItem orderItem = new OrderItem();

foreach(OrderItemInfo item in order.OrderItemList) {

orderItem.AddOrderItem(item.ProductID,item.Number,nOrderFormID);

}

}

///display the hint message

Response.Write("<script>alert("You have succeeded in committing the
order!")</script>");

}

Here, we first obtain the order info out of the Session variable, then submit it to the remote SQL Server database, and at last create an order. That's it.

Author's Note: First, because this is just a demonstration application, we have not attached all the order-related info to the popular online payment systems, such as PayPal or any other famous system. Thus, in practical development, you will have to do this by yourself. Second, to grasp all the data relations introduced in the above several modules you should carefully study the relationships between some classes such as OrderForm, OrderInfo, and OrderItemInfo.

blog comments powered by Disqus
ASP.NET ARTICLES

- Implementing ASP.NET 4.0 Page.MetaDescriptio...
- ASP.Net Development Tips
- Intro to Sessions in ASP.Net
- Google Maps API Introduction in ASP.NET usin...
- Creating an ASP.NET 3.5 Gridview Image Galle...
- Encrypt QueryString in ASP.NET 3.5 using VB....
- ASP.NET 3.5 Drop Down List Controls
- Connect to Access Database with ASP.Net
- Secure Audio Streaming with ASP.Net and Flash
- Dynamic Sitemap and Navigation in ASP.Net
- Implement Gzip and Deflate Compression in AS...
- Run ASP.Net in Ubuntu with Apache
- ASP.Net Mono Website Contact Forms
- ASP.Net URL Rewriting Methods
- Murach`s ASP.NET 4 Web Programming with C# 2...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 5 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials