<?xml version="1.0" encoding="utf-8"?>
<%@ page inherits="shop.site" src="cs/site.aspx.cs" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>David Millington 2006 - DevArticles GridView Shop Example</title>
<meta name="keywords" content="gridview,shop,demonstration,example,dev,articles,david,millington" />
<meta name="description" content="C# GridView Shop Example for DevArticles by David Millington" />
<link rel="stylesheet" rev="stylesheet" href="css/framework.css" type="text/css" media="screen" charset="utf-8" />
</head>
<body>
<form runat="server">
<div class="header">
<img src="images/header.png" width="760px" height="80px" alt="David Millingtons DevArticles Demonstration Shop" />
</div>
<div class="mainmenu">
<a href="#">Home</a>
</div>
<div class="content">
<div class="left">
<fieldset class="products">
<legend>Products</legend>
<asp:GridView id="gvProducts"
AutoGenerateColumns="false"
EmptyDataText="~There are no products in this shop~"
AllowPaging="True"
ShowHeader="False"
ShowFooter="False"
DataKeyNames="id"
PageSize="3"
onPageIndexChanging="gvProducts_PageIndexChanging"
OnRowDataBound="gvProducts_RowDataBound"
GridLines="None"
CellPadding="10"
CssClass="shopgrid"
RowStyle-CssClass="shopgridrow"
AlternatingRowStyle-CssClass="shopgridrowalt"
PagerStyle-CssClass="shopgridpager"
EmptyDataRowStyle-CssClass="shopgridempty"
runat="server">
<Columns>
<asp:ImageField DataImageUrlField="thumb" alternatetext="Product Thumbnail" readonly="true" />
<asp:TemplateField ItemStyle-Width="100px">
<ItemTemplate>
<h3><asp:Literal id="litItemName" runat="server" /></h3>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Literal id="litPrice" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<input type="submit" id="ibBuy" runat="server" value="Add to basket" OnServerClick="shopBuy_OnServerClick" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</fieldset>
</div>
<div class="right">
<fieldset class="basket">
<legend>Basket</legend>
<asp:GridView id="gvBasket"
AutoGenerateColumns="false"
EmptyDataText="~Basket is empty~"
AllowPaging="False"
ShowHeader="True"
ShowFooter="True"
DataKeyNames="id"
OnRowDataBound="gvBasket_RowDataBound"
GridLines="None"
CellPadding="10"
CssClass="shopgrid"
RowStyle-CssClass="shopgridrow"
AlternatingRowStyle-CssClass="shopgridrowalt"
HeaderStyle-CssClass="shopgridheader"
FooterStyle-CssClass="shopgridfooter"
EmptyDataRowStyle-CssClass="shopgridempty"
runat="server">
<Columns>
<asp:TemplateField HeaderText="Item">
<ItemTemplate>
<h3><asp:Literal id="litItemName" runat="server" /></h3>
</ItemTemplate>
<FooterTemplate>
Delivery Charges
<br /><hr />
<b>Total</b>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Quantity" ItemStyle-Width="50px">
<ItemTemplate>
<input type="text" id="itProductQuantity" runat="server" maxlength="3" style="width:100%;" />
</ItemTemplate>
<FooterTemplate>
1
<br /><hr />
<asp:Literal id="litTotalQuantity" runat="server" />
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price" ItemStyle-Width="50px">
<ItemTemplate>
<asp:Literal id="litPrice" runat="server" />
</ItemTemplate>
<FooterTemplate>
<asp:Literal id="litDeliveryPrice" runat="server" />
<br /><hr />
<asp:Literal id="litTotalPrice" runat="server" />
</FooterTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<div class="basketcontrols">
<input type="submit" id="ibEmptyBasket" runat="server" value="Empty Basket" OnServerClick="shopClearBasket_OnServerClick" Visible="false" />
<input type="submit" id="ibUpdateBasketQuantities" runat="server" value="Update Quantities" OnServerClick="shopUpdateBasketQuantities_OnServerClick" Visible="false" />
<input type="submit" id="ibBasketCheckout" runat="server" value="Pay With PayPal" OnServerClick="shopBasketCheckout_OnServerClick" Visible="false" />
</div>
</fieldset>
</div>
<div class="clear"></div>
</div>
<div class="footer">
<p class="bold_grey">Designed and Developed by <a href="http://www.davidmillington.net">David Millington</a> 2006</p>
</div>
</form>
</body>
</html>
--------------------------------------------------------------------------------------------------------------
html, body
{
width: 100%;
margin: 0;
padding: 0;
font: normal normal normal 12px Arial, Verdana, Sans-Serif;
color: #000000;
text-align: center;
background: #888892 url() no-repeat scroll top left;
}
form
{
width: 780px;
margin: 0 auto;
background: #f8a904 url() no-repeat scroll top left;
}
/* Defaults */
img
{
border: none;
}
p, table, td, tr
{
font: normal normal normal 12px Arial, Verdana, Sans-Serif;
color: #000000;
}
h1, .h1
{
font: normal normal normal 18px Arial, Verdana, Sans-Serif;
}
h2, .h2
{
font: normal normal normal 16px Arial, Verdana, Sans-Serif;
}
h3, .h3
{
margin: 0;
padding: 0;
font: normal normal normal 12px Arial, Verdana, Sans-Serif;
}
/*
Main Page Elements
*/
.header
{
width: 100%;
padding: 10px;
}
.mainmenu
{
width: 100%;
padding: 5px 0px;
}
.content
{
width: 780px;
}
.footer
{
width: 100%;
padding: 5px 0px;
}
/*
Two Main Fieldsets
*/
.left
{
float: left;
width: 500px;
background: #fff url() no-repeat scroll top left;
}
.right
{
float: left;
width: 280px;
background: #f8a904 url() no-repeat scroll top left;
}
fieldset
{
width: 97%;
margin: 0 auto;
padding: 0;
text-align: left;
border: 1px #da821e solid;
}
legend
{
margin: 0;
padding: 5px;
font-weight: 900;
}
.clear
{
clear: both;
}
/*
The GridViews get rendered as tables so we can format them by setting table styles
*/
.shopgrid
{
width: 100%;
}
.shopgridheader, .shopgridfooter
{
background: #fff url() no-repeat scroll top left;
}
.shopgridrow
{
}
.shopgridrowalt
{
background: #da821e url() no-repeat scroll top left;
}
.shopgridpager
{
text-align: center;
}
.shopgridpager a
{
font-weight: 900;
}
.shopgridempty
{
text-align: center;
}
.basketcontrols
{
text-align: center;
padding: 10px;
}
--------------------------------------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using System.ComponentModel;
using System.IO;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.Mail;
using System.Net;
using System.Drawing;
namespace shop
{
public class site : Page
{
// Shop Related
protected GridView gvBasket;
protected GridView gvProducts;
// Empty Basket Button
protected HtmlInputButton ibEmptyBasket;
// Update Quantities Button
protected HtmlInputButton ibUpdateBasketQuantities;
// Checkout Button
protected HtmlInputButton ibBasketCheckout;
public site()
{
Page.Init += new System.EventHandler(Page_Init);
}
private void Page_Init(object sender, EventArgs e)
{
this.Load += new EventHandler(this.Page_Load);
}
private void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
populateProducts();
updateShopBasket();
}
}
/// <summary>
/// Creates the initial DataTable that will store products
/// </summary>
private DataTable createProductDT()
{
DataTable dtProducts = new DataTable();
DataColumn productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.Int32");
productColumn.ColumnName = "id";
productColumn.Unique = true;
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.String");
productColumn.ColumnName = "thumb";
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.String");
productColumn.ColumnName = "name";
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.Double");
productColumn.ColumnName = "price";
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.Int32");
productColumn.ColumnName = "quantity";
dtProducts.Columns.Add(productColumn);
// Make "id" the primary key
DataColumn[] pkColumns = new DataColumn[1];
pkColumns[0] = dtProducts.Columns["id"];
dtProducts.PrimaryKey = pkColumns;
return dtProducts;
}
/// <summary>
/// Populate the Products GridView
/// </summary>
private void populateProducts()
{
// Create the basic structure
DataTable dtProducts = createProductDT();
// Add the products to it
// Create the initial row
DataRow aProduct = dtProducts.NewRow();
aProduct["id"] = 11;
aProduct["thumb"] = "images/widget0.jpg";
aProduct["name"] = "Red Widget";
aProduct["price"] = 19.99;
dtProducts.Rows.Add(aProduct);
// Re-use the row to add a new product
aProduct = dtProducts.NewRow();
aProduct["id"] = 22;
aProduct["thumb"] = "images/widget1.jpg";
aProduct["name"] = "Green Widget";
aProduct["price"] = 50.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 33;
aProduct["thumb"] = "images/widget2.jpg";
aProduct["name"] = "Blue Widget";
aProduct["price"] = 24.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 44;
aProduct["thumb"] = "images/widget3.jpg";
aProduct["name"] = "Small Widget";
aProduct["price"] = 16.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 55;
aProduct["thumb"] = "images/widget4.jpg";
aProduct["name"] = "Medium Widget";
aProduct["price"] = 26.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 66;
aProduct["thumb"] = "images/widget5.jpg";
aProduct["name"] = "Large Widget";
aProduct["price"] = 54.99;
dtProducts.Rows.Add(aProduct);
// Bind the DataTable to the Products GridView
gvProducts.DataSource = dtProducts;
gvProducts.DataBind();
// Store the products in Session
// NOTE: You wouldnt really do this with a full-fledged shop and would more than likely
// use database queries to query a database each time you access this DataTable
// This Session stored DataTable is only here for the convenience of this article
Session["dtProducts"] = dtProducts;
}
/// <summary>
/// Gets called when we choose a new page via the paging links at the bottom of the GridView
///
/// Sets the PageIndex to the Index and then rebinds the DataSource to the GridView
/// </summary>
protected void gvProducts_PageIndexChanging(Object sender, GridViewPageEventArgs e)
{
gvProducts.PageIndex = e.NewPageIndex;
populateProducts();
}
/// <summary>
/// Populate each Product GridView row
///
/// Because we have controls inside each cell we can populate them as each row item is bound
/// Read more about this event:
/// http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rowdatabound(VS.80).aspx
/// </summary>
protected void gvProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
switch( e.Row.RowType )
{
case DataControlRowType.DataRow:
// Name/Desc
((Literal)e.Row.FindControl("litItemName")).Text = Convert.ToString(((DataRowView)e.Row.DataItem)["name"]);
// Price
((Literal)e.Row.FindControl("litPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(((DataRowView)e.Row.DataItem)["price"]));
break;
}
}
/// <summary>
/// "Buy" button for each product row
/// </summary>
protected void shopBuy_OnServerClick(object source, EventArgs e)
{
/*
* This looks pretty hairy but all we're doing is getting the button (source) and using that
* to get access to the GridViewRow that its in (Parent.NamingContainer).
* Finally we use that to get the RowIndex which we can then use as an index into the DataKey array
* that is associated with the GridView
* Note that the RowIndex isnt actually the productId, its just an integer index
*/
int index = ((GridViewRow)((HtmlInputButton)source).Parent.NamingContainer).RowIndex;
/*
* Fetch the productId using the RowIndex we got from the GridViewRow
*/
addToBasket(Convert.ToInt32(gvProducts.DataKeys[index].Value));
}
/// <summary>
/// Fetch / Create the product list
/// </summary>
private DataTable getProductsDt()
{
if(Session["dtProducts"] == null)
{
// If the DataTable doesnt exist in the Session then we can create it now
populateProducts();
}
return (DataTable)Session["dtProducts"];
}
/// <summary>
/// Fetch / Create the basket
///
/// We store the entire shop basket in Session state and then pull it out each time we need to modify it.
/// This function gets called each time we want to access the Baskets data.
/// </summary>
private DataTable getBasketDt()
{
DataTable dtBasket;
if(Session["dtBasket"] == null)
{
// If the DataTable doesnt exist in the Session then we can create it now
dtBasket = createProductDT();
}
else
{
// Else it does exist in Session so just pull our current one out
dtBasket = (DataTable)Session["dtBasket"];
}
return dtBasket;
}
/// <summary>
/// Populate each Basket GridView row
///
/// Because we have controls inside each cell we can populate them as each row item is bound
/// Read more about this event:
/// http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rowdatabound(VS.80).aspx
/// </summary>
protected void gvBasket_RowDataBound(object sender, GridViewRowEventArgs e)
{
switch( e.Row.RowType )
{
case DataControlRowType.DataRow:
// Name/Desc
((Literal)e.Row.FindControl("litItemName")).Text = Convert.ToString(((DataRowView)e.Row.DataItem)["name"]);
// Quantity
string quantity = Convert.ToString(((DataRowView)e.Row.DataItem)["quantity"]);
((HtmlInputText)e.Row.FindControl("itProductQuantity")).Value = quantity;
// Price
((Literal)e.Row.FindControl("litPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(((DataRowView)e.Row.DataItem)["price"]) * Convert.ToInt32(quantity));
break;
case DataControlRowType.Footer:
DataTable dtShop = getBasketDt();
double total = 0.00;
for(int i = 0; i < dtShop.Rows.Count; i++)
{
total += Convert.ToInt32(dtShop.Rows[i]["quantity"]) * Convert.ToDouble(dtShop.Rows[i]["price"]);
}
((Literal)e.Row.FindControl("litTotalQuantity")).Text = Convert.ToString(dtShop.Compute("SUM(quantity)", ""));
((Literal)e.Row.FindControl("litDeliveryPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(calcDeliveryCost(total)));
((Literal)e.Row.FindControl("litTotalPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(calcDeliveryCost(total)) + total);
break;
}
}
/// <summary>
/// This is a simple delivery cost calculator. It works off the basket total and not each item individually.
/// In a more robust system each item would have its own delivery methods and delivery costs which would
/// account for its dimensions, weight, particular requirements etc.
/// </summary>
private string calcDeliveryCost(double total)
{
string deliveryCost = "0.00";
if(total < 30.00)
{
deliveryCost = "4.95";
}
else if(total >= 30.00 && total < 70.00)
{
deliveryCost = "7.95";
}
else if(total >= 70.00 && total < 145.00)
{
deliveryCost = "10.95";
}
else if(total >= 145.00)
{
deliveryCost = "0.00";
}
return deliveryCost;
}
/// <summary>
/// Re-binds the basket GridView
/// </summary>
private void updateShopBasket()
{
gvBasket.DataSource = getBasketDt();
gvBasket.DataBind();
/*
* Set our three basket buttons to be visible if there are any items in our basket
* afterall there is no point letting them perform any of the three actions if there are no items
*/
ibEmptyBasket.Visible = ibUpdateBasketQuantities.Visible = ibBasketCheckout.Visible = gvBasket.Rows.Count > 0;
}
/// <summary>
/// Adds an item to the basket.
/// If the item is already in the basket then it simply updates the quantity.
/// </summary>
protected void addToBasket(int productID)
{
DataTable dtBasket = getBasketDt();
// Loop through the basket and check if this item already exists
bool found = false;
for(int i = 0; i < dtBasket.Rows.Count; i++)
{
if(Convert.ToInt32(dtBasket.Rows[i]["id"]) == productID)
{
// increment the quantity and mark as found
dtBasket.Rows[i]["quantity"] = Convert.ToInt32(dtBasket.Rows[i]["quantity"]) + 1;
found = true;
// break out of the for loop as we have already found the item at this point
break;
}
}
// If the item is not found then add it as a new row
if(!found)
{
/*
* At this point you would use the productId in a database select statement to pull the relevent
* information that is needed to add a new row to the basket
* However because we're not using a database in this article we will pull the product DataTable we stored
* ealier and get the information we require from that instead
*/
DataTable dtProducts = getProductsDt();
DataRow drProduct = dtProducts.Rows.Find(productID);
// Now we've got the data we need from our datasource we can add a new row to the basket
DataRow newRow = dtBasket.NewRow();
newRow["id"] = drProduct["id"];
newRow["name"] = drProduct["name"];
newRow["price"] = drProduct["price"];
newRow["quantity"] = 1;
dtBasket.Rows.Add(newRow);
}
// Store the newly updated basket back in the Session
Session["dtBasket"] = dtBasket;
// Update the basket i.e. re-bind it
updateShopBasket();
}
/// <summary>
/// Update the quantities in the basket removing any items with 0 as the quantity
/// </summary>
protected void shopUpdateBasketQuantities_OnServerClick(object source, EventArgs e)
{
DataTable dtBasket = getBasketDt();
/*
* Go through each row in the basket display and compare it to the basket in the session
* Compare the quantities and modify the Session basket as required to reflect
* any changes made by the customer via the quanitites input boxes
*/
foreach(GridViewRow row in gvBasket.Rows)
{
int index = row.RowIndex;
int productId = Convert.ToInt32(gvBasket.DataKeys[index].Value);
// Find the product in the Session Basket
DataRow drProduct = dtBasket.Rows.Find(productId);
if(drProduct != null)
{
// Read the data from the Quantity Text Box
HtmlInputText itQuant = (HtmlInputText)row.FindControl("itProductQuantity");
// Try and convert the value to an int
try
{
int quant = Convert.ToInt32(itQuant.Value);
/*
* If the value converted to an Int successfully we still need to check
* that its not a minus number, otherwise we might end up owing the customer money!
*/
if(quant > 0)
{
drProduct["quantity"] = quant;
}
else
{
drProduct.Delete();
}
}
catch
{
// If we cant convert it to an Int then just leave it as it was i.e. make no changes
}
}
}
if(dtBasket.Rows.Count > 0)
{
// IF we still have items in the basket then update the Session Basket
Session["dtBasket"] = dtBasket;
}
else
{
// ELSE delete the Session Basket
Session.Contents.Remove("dtBasket");
}
// Refresh the basket display
updateShopBasket();
}
/// <summary>
/// Clear the entire basket
/// </summary>
protected void shopClearBasket_OnServerClick(object source, EventArgs e)
{
Session.Contents.Remove("dtBasket");
updateShopBasket();
}
/// <summary>
/// CHECKOUT
///
/// Pull all the basket data out of the Session and POST it to the external payment service
///
/// Doing it this way means that the address wont change for the first landing page of the service
///
/// This isnt usually a problem since they will have to login at which point the address will change
/// to match their actual location.
/// </summary>
protected void shopBasketCheckout_OnServerClick(object source, EventArgs e)
{
string postData = "";
postData += "currency_code=GBP";
postData += "&cmd=_cart";
postData += "&business=davidmillingtons@hotmail.com";
postData += "&upload=1";
postData += "&cancel_return=www.davidmillington.net";
DataTable dtBasket = getBasketDt();
double total = 0.00;
for(int i = 0; i < dtBasket.Rows.Count; i++)
{
postData += "&item_name_" + (i + 1) + "=" + dtBasket.Rows[i]["name"];
postData += "&quantity_" + (i + 1) + "=" + dtBasket.Rows[i]["quantity"];
postData += "&amount_" + (i + 1) + "=" + Convert.ToDouble(dtBasket.Rows[i]["price"]);
total += (Convert.ToDouble(dtBasket.Rows[i]["price"]) * Convert.ToInt32(dtBasket.Rows[i]["quantity"]));
if(i == dtBasket.Rows.Count - 1)
{
postData += "&shipping_" + (i + 1) + "=" + calcDeliveryCost(total);
}
else
{
postData += "&shipping_" + (i + 1) + "=0.00";
}
postData += "&shipping2_" + (i + 1) + "=0.00";
postData += "&handling_" + (i + 1) + "=0.00";
}
postData += "&handling=" + calcDeliveryCost(total);
// Response.Write("<!--" + postData + "-->");
byte[] data = Encoding.ASCII.GetBytes(postData);
HttpWebRequest ppRequest = (HttpWebRequest)WebRequest.Create("https://www.paypal.com/cgi-bin/webscr");;
ppRequest.Method = "POST";
ppRequest.ContentType = "application/x-www-form-urlencoded";
ppRequest.ContentLength = data.Length;
// Send
Stream ppStream = ppRequest.GetRequestStream();
ppStream.Write(data, 0, data.Length);
ppStream.Close();
// Receive :¬(
HttpWebResponse ppResponse = (HttpWebResponse)ppRequest.GetResponse();
StreamReader sr = new StreamReader(ppResponse.GetResponseStream());
string strResult = sr.ReadToEnd();
sr.Close();
// Write to screen
Response.Clear();
Response.Write(strResult);
Response.End();
}
} // EOC
} // EON
-------------------------------------------------------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation defaultLanguage="C#" debug="true" />
<customErrors mode="Off" />
<authorization>
<allow users="*" />
</authorization>
<trace
enabled="false"
requestLimit="10"
pageOutput="false"
traceMode="SortByTime"
localOnly="true"
/>
<globalization
requestEncoding="utf-8"
responseEncoding="utf-8"
/>
<httpRuntime
executionTimeout="90"
maxRequestLength="4096"
useFullyQualifiedRedirectUrl="false"
minFreeThreads="8"
minLocalRequestFreeThreads="4"
appRequestQueueLimit="100"
/>
</system.web>
<appSettings>
</appSettings>
</configuration>
<%@ page inherits="shop.site" src="cs/site.aspx.cs" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>David Millington 2006 - DevArticles GridView Shop Example</title>
<meta name="keywords" content="gridview,shop,demonstration,example,dev,articles,david,millington" />
<meta name="description" content="C# GridView Shop Example for DevArticles by David Millington" />
<link rel="stylesheet" rev="stylesheet" href="css/framework.css" type="text/css" media="screen" charset="utf-8" />
</head>
<body>
<form runat="server">
<div class="header">
<img src="images/header.png" width="760px" height="80px" alt="David Millingtons DevArticles Demonstration Shop" />
</div>
<div class="mainmenu">
<a href="#">Home</a>
</div>
<div class="content">
<div class="left">
<fieldset class="products">
<legend>Products</legend>
<asp:GridView id="gvProducts"
AutoGenerateColumns="false"
EmptyDataText="~There are no products in this shop~"
AllowPaging="True"
ShowHeader="False"
ShowFooter="False"
DataKeyNames="id"
PageSize="3"
onPageIndexChanging="gvProducts_PageIndexChanging"
OnRowDataBound="gvProducts_RowDataBound"
GridLines="None"
CellPadding="10"
CssClass="shopgrid"
RowStyle-CssClass="shopgridrow"
AlternatingRowStyle-CssClass="shopgridrowalt"
PagerStyle-CssClass="shopgridpager"
EmptyDataRowStyle-CssClass="shopgridempty"
runat="server">
<Columns>
<asp:ImageField DataImageUrlField="thumb" alternatetext="Product Thumbnail" readonly="true" />
<asp:TemplateField ItemStyle-Width="100px">
<ItemTemplate>
<h3><asp:Literal id="litItemName" runat="server" /></h3>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Literal id="litPrice" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<input type="submit" id="ibBuy" runat="server" value="Add to basket" OnServerClick="shopBuy_OnServerClick" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</fieldset>
</div>
<div class="right">
<fieldset class="basket">
<legend>Basket</legend>
<asp:GridView id="gvBasket"
AutoGenerateColumns="false"
EmptyDataText="~Basket is empty~"
AllowPaging="False"
ShowHeader="True"
ShowFooter="True"
DataKeyNames="id"
OnRowDataBound="gvBasket_RowDataBound"
GridLines="None"
CellPadding="10"
CssClass="shopgrid"
RowStyle-CssClass="shopgridrow"
AlternatingRowStyle-CssClass="shopgridrowalt"
HeaderStyle-CssClass="shopgridheader"
FooterStyle-CssClass="shopgridfooter"
EmptyDataRowStyle-CssClass="shopgridempty"
runat="server">
<Columns>
<asp:TemplateField HeaderText="Item">
<ItemTemplate>
<h3><asp:Literal id="litItemName" runat="server" /></h3>
</ItemTemplate>
<FooterTemplate>
Delivery Charges
<br /><hr />
<b>Total</b>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Quantity" ItemStyle-Width="50px">
<ItemTemplate>
<input type="text" id="itProductQuantity" runat="server" maxlength="3" style="width:100%;" />
</ItemTemplate>
<FooterTemplate>
1
<br /><hr />
<asp:Literal id="litTotalQuantity" runat="server" />
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price" ItemStyle-Width="50px">
<ItemTemplate>
<asp:Literal id="litPrice" runat="server" />
</ItemTemplate>
<FooterTemplate>
<asp:Literal id="litDeliveryPrice" runat="server" />
<br /><hr />
<asp:Literal id="litTotalPrice" runat="server" />
</FooterTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<div class="basketcontrols">
<input type="submit" id="ibEmptyBasket" runat="server" value="Empty Basket" OnServerClick="shopClearBasket_OnServerClick" Visible="false" />
<input type="submit" id="ibUpdateBasketQuantities" runat="server" value="Update Quantities" OnServerClick="shopUpdateBasketQuantities_OnServerClick" Visible="false" />
<input type="submit" id="ibBasketCheckout" runat="server" value="Pay With PayPal" OnServerClick="shopBasketCheckout_OnServerClick" Visible="false" />
</div>
</fieldset>
</div>
<div class="clear"></div>
</div>
<div class="footer">
<p class="bold_grey">Designed and Developed by <a href="http://www.davidmillington.net">David Millington</a> 2006</p>
</div>
</form>
</body>
</html>
--------------------------------------------------------------------------------------------------------------
html, body
{
width: 100%;
margin: 0;
padding: 0;
font: normal normal normal 12px Arial, Verdana, Sans-Serif;
color: #000000;
text-align: center;
background: #888892 url() no-repeat scroll top left;
}
form
{
width: 780px;
margin: 0 auto;
background: #f8a904 url() no-repeat scroll top left;
}
/* Defaults */
img
{
border: none;
}
p, table, td, tr
{
font: normal normal normal 12px Arial, Verdana, Sans-Serif;
color: #000000;
}
h1, .h1
{
font: normal normal normal 18px Arial, Verdana, Sans-Serif;
}
h2, .h2
{
font: normal normal normal 16px Arial, Verdana, Sans-Serif;
}
h3, .h3
{
margin: 0;
padding: 0;
font: normal normal normal 12px Arial, Verdana, Sans-Serif;
}
/*
Main Page Elements
*/
.header
{
width: 100%;
padding: 10px;
}
.mainmenu
{
width: 100%;
padding: 5px 0px;
}
.content
{
width: 780px;
}
.footer
{
width: 100%;
padding: 5px 0px;
}
/*
Two Main Fieldsets
*/
.left
{
float: left;
width: 500px;
background: #fff url() no-repeat scroll top left;
}
.right
{
float: left;
width: 280px;
background: #f8a904 url() no-repeat scroll top left;
}
fieldset
{
width: 97%;
margin: 0 auto;
padding: 0;
text-align: left;
border: 1px #da821e solid;
}
legend
{
margin: 0;
padding: 5px;
font-weight: 900;
}
.clear
{
clear: both;
}
/*
The GridViews get rendered as tables so we can format them by setting table styles
*/
.shopgrid
{
width: 100%;
}
.shopgridheader, .shopgridfooter
{
background: #fff url() no-repeat scroll top left;
}
.shopgridrow
{
}
.shopgridrowalt
{
background: #da821e url() no-repeat scroll top left;
}
.shopgridpager
{
text-align: center;
}
.shopgridpager a
{
font-weight: 900;
}
.shopgridempty
{
text-align: center;
}
.basketcontrols
{
text-align: center;
padding: 10px;
}
--------------------------------------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using System.ComponentModel;
using System.IO;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.Mail;
using System.Net;
using System.Drawing;
namespace shop
{
public class site : Page
{
// Shop Related
protected GridView gvBasket;
protected GridView gvProducts;
// Empty Basket Button
protected HtmlInputButton ibEmptyBasket;
// Update Quantities Button
protected HtmlInputButton ibUpdateBasketQuantities;
// Checkout Button
protected HtmlInputButton ibBasketCheckout;
public site()
{
Page.Init += new System.EventHandler(Page_Init);
}
private void Page_Init(object sender, EventArgs e)
{
this.Load += new EventHandler(this.Page_Load);
}
private void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
populateProducts();
updateShopBasket();
}
}
/// <summary>
/// Creates the initial DataTable that will store products
/// </summary>
private DataTable createProductDT()
{
DataTable dtProducts = new DataTable();
DataColumn productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.Int32");
productColumn.ColumnName = "id";
productColumn.Unique = true;
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.String");
productColumn.ColumnName = "thumb";
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.String");
productColumn.ColumnName = "name";
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.Double");
productColumn.ColumnName = "price";
dtProducts.Columns.Add(productColumn);
productColumn = new DataColumn();
productColumn.DataType = System.Type.GetType("System.Int32");
productColumn.ColumnName = "quantity";
dtProducts.Columns.Add(productColumn);
// Make "id" the primary key
DataColumn[] pkColumns = new DataColumn[1];
pkColumns[0] = dtProducts.Columns["id"];
dtProducts.PrimaryKey = pkColumns;
return dtProducts;
}
/// <summary>
/// Populate the Products GridView
/// </summary>
private void populateProducts()
{
// Create the basic structure
DataTable dtProducts = createProductDT();
// Add the products to it
// Create the initial row
DataRow aProduct = dtProducts.NewRow();
aProduct["id"] = 11;
aProduct["thumb"] = "images/widget0.jpg";
aProduct["name"] = "Red Widget";
aProduct["price"] = 19.99;
dtProducts.Rows.Add(aProduct);
// Re-use the row to add a new product
aProduct = dtProducts.NewRow();
aProduct["id"] = 22;
aProduct["thumb"] = "images/widget1.jpg";
aProduct["name"] = "Green Widget";
aProduct["price"] = 50.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 33;
aProduct["thumb"] = "images/widget2.jpg";
aProduct["name"] = "Blue Widget";
aProduct["price"] = 24.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 44;
aProduct["thumb"] = "images/widget3.jpg";
aProduct["name"] = "Small Widget";
aProduct["price"] = 16.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 55;
aProduct["thumb"] = "images/widget4.jpg";
aProduct["name"] = "Medium Widget";
aProduct["price"] = 26.99;
dtProducts.Rows.Add(aProduct);
aProduct = dtProducts.NewRow();
aProduct["id"] = 66;
aProduct["thumb"] = "images/widget5.jpg";
aProduct["name"] = "Large Widget";
aProduct["price"] = 54.99;
dtProducts.Rows.Add(aProduct);
// Bind the DataTable to the Products GridView
gvProducts.DataSource = dtProducts;
gvProducts.DataBind();
// Store the products in Session
// NOTE: You wouldnt really do this with a full-fledged shop and would more than likely
// use database queries to query a database each time you access this DataTable
// This Session stored DataTable is only here for the convenience of this article
Session["dtProducts"] = dtProducts;
}
/// <summary>
/// Gets called when we choose a new page via the paging links at the bottom of the GridView
///
/// Sets the PageIndex to the Index and then rebinds the DataSource to the GridView
/// </summary>
protected void gvProducts_PageIndexChanging(Object sender, GridViewPageEventArgs e)
{
gvProducts.PageIndex = e.NewPageIndex;
populateProducts();
}
/// <summary>
/// Populate each Product GridView row
///
/// Because we have controls inside each cell we can populate them as each row item is bound
/// Read more about this event:
/// http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rowdatabound(VS.80).aspx
/// </summary>
protected void gvProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
switch( e.Row.RowType )
{
case DataControlRowType.DataRow:
// Name/Desc
((Literal)e.Row.FindControl("litItemName")).Text = Convert.ToString(((DataRowView)e.Row.DataItem)["name"]);
// Price
((Literal)e.Row.FindControl("litPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(((DataRowView)e.Row.DataItem)["price"]));
break;
}
}
/// <summary>
/// "Buy" button for each product row
/// </summary>
protected void shopBuy_OnServerClick(object source, EventArgs e)
{
/*
* This looks pretty hairy but all we're doing is getting the button (source) and using that
* to get access to the GridViewRow that its in (Parent.NamingContainer).
* Finally we use that to get the RowIndex which we can then use as an index into the DataKey array
* that is associated with the GridView
* Note that the RowIndex isnt actually the productId, its just an integer index
*/
int index = ((GridViewRow)((HtmlInputButton)source).Parent.NamingContainer).RowIndex;
/*
* Fetch the productId using the RowIndex we got from the GridViewRow
*/
addToBasket(Convert.ToInt32(gvProducts.DataKeys[index].Value));
}
/// <summary>
/// Fetch / Create the product list
/// </summary>
private DataTable getProductsDt()
{
if(Session["dtProducts"] == null)
{
// If the DataTable doesnt exist in the Session then we can create it now
populateProducts();
}
return (DataTable)Session["dtProducts"];
}
/// <summary>
/// Fetch / Create the basket
///
/// We store the entire shop basket in Session state and then pull it out each time we need to modify it.
/// This function gets called each time we want to access the Baskets data.
/// </summary>
private DataTable getBasketDt()
{
DataTable dtBasket;
if(Session["dtBasket"] == null)
{
// If the DataTable doesnt exist in the Session then we can create it now
dtBasket = createProductDT();
}
else
{
// Else it does exist in Session so just pull our current one out
dtBasket = (DataTable)Session["dtBasket"];
}
return dtBasket;
}
/// <summary>
/// Populate each Basket GridView row
///
/// Because we have controls inside each cell we can populate them as each row item is bound
/// Read more about this event:
/// http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rowdatabound(VS.80).aspx
/// </summary>
protected void gvBasket_RowDataBound(object sender, GridViewRowEventArgs e)
{
switch( e.Row.RowType )
{
case DataControlRowType.DataRow:
// Name/Desc
((Literal)e.Row.FindControl("litItemName")).Text = Convert.ToString(((DataRowView)e.Row.DataItem)["name"]);
// Quantity
string quantity = Convert.ToString(((DataRowView)e.Row.DataItem)["quantity"]);
((HtmlInputText)e.Row.FindControl("itProductQuantity")).Value = quantity;
// Price
((Literal)e.Row.FindControl("litPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(((DataRowView)e.Row.DataItem)["price"]) * Convert.ToInt32(quantity));
break;
case DataControlRowType.Footer:
DataTable dtShop = getBasketDt();
double total = 0.00;
for(int i = 0; i < dtShop.Rows.Count; i++)
{
total += Convert.ToInt32(dtShop.Rows[i]["quantity"]) * Convert.ToDouble(dtShop.Rows[i]["price"]);
}
((Literal)e.Row.FindControl("litTotalQuantity")).Text = Convert.ToString(dtShop.Compute("SUM(quantity)", ""));
((Literal)e.Row.FindControl("litDeliveryPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(calcDeliveryCost(total)));
((Literal)e.Row.FindControl("litTotalPrice")).Text = String.Format("{0:C2}", Convert.ToDouble(calcDeliveryCost(total)) + total);
break;
}
}
/// <summary>
/// This is a simple delivery cost calculator. It works off the basket total and not each item individually.
/// In a more robust system each item would have its own delivery methods and delivery costs which would
/// account for its dimensions, weight, particular requirements etc.
/// </summary>
private string calcDeliveryCost(double total)
{
string deliveryCost = "0.00";
if(total < 30.00)
{
deliveryCost = "4.95";
}
else if(total >= 30.00 && total < 70.00)
{
deliveryCost = "7.95";
}
else if(total >= 70.00 && total < 145.00)
{
deliveryCost = "10.95";
}
else if(total >= 145.00)
{
deliveryCost = "0.00";
}
return deliveryCost;
}
/// <summary>
/// Re-binds the basket GridView
/// </summary>
private void updateShopBasket()
{
gvBasket.DataSource = getBasketDt();
gvBasket.DataBind();
/*
* Set our three basket buttons to be visible if there are any items in our basket
* afterall there is no point letting them perform any of the three actions if there are no items
*/
ibEmptyBasket.Visible = ibUpdateBasketQuantities.Visible = ibBasketCheckout.Visible = gvBasket.Rows.Count > 0;
}
/// <summary>
/// Adds an item to the basket.
/// If the item is already in the basket then it simply updates the quantity.
/// </summary>
protected void addToBasket(int productID)
{
DataTable dtBasket = getBasketDt();
// Loop through the basket and check if this item already exists
bool found = false;
for(int i = 0; i < dtBasket.Rows.Count; i++)
{
if(Convert.ToInt32(dtBasket.Rows[i]["id"]) == productID)
{
// increment the quantity and mark as found
dtBasket.Rows[i]["quantity"] = Convert.ToInt32(dtBasket.Rows[i]["quantity"]) + 1;
found = true;
// break out of the for loop as we have already found the item at this point
break;
}
}
// If the item is not found then add it as a new row
if(!found)
{
/*
* At this point you would use the productId in a database select statement to pull the relevent
* information that is needed to add a new row to the basket
* However because we're not using a database in this article we will pull the product DataTable we stored
* ealier and get the information we require from that instead
*/
DataTable dtProducts = getProductsDt();
DataRow drProduct = dtProducts.Rows.Find(productID);
// Now we've got the data we need from our datasource we can add a new row to the basket
DataRow newRow = dtBasket.NewRow();
newRow["id"] = drProduct["id"];
newRow["name"] = drProduct["name"];
newRow["price"] = drProduct["price"];
newRow["quantity"] = 1;
dtBasket.Rows.Add(newRow);
}
// Store the newly updated basket back in the Session
Session["dtBasket"] = dtBasket;
// Update the basket i.e. re-bind it
updateShopBasket();
}
/// <summary>
/// Update the quantities in the basket removing any items with 0 as the quantity
/// </summary>
protected void shopUpdateBasketQuantities_OnServerClick(object source, EventArgs e)
{
DataTable dtBasket = getBasketDt();
/*
* Go through each row in the basket display and compare it to the basket in the session
* Compare the quantities and modify the Session basket as required to reflect
* any changes made by the customer via the quanitites input boxes
*/
foreach(GridViewRow row in gvBasket.Rows)
{
int index = row.RowIndex;
int productId = Convert.ToInt32(gvBasket.DataKeys[index].Value);
// Find the product in the Session Basket
DataRow drProduct = dtBasket.Rows.Find(productId);
if(drProduct != null)
{
// Read the data from the Quantity Text Box
HtmlInputText itQuant = (HtmlInputText)row.FindControl("itProductQuantity");
// Try and convert the value to an int
try
{
int quant = Convert.ToInt32(itQuant.Value);
/*
* If the value converted to an Int successfully we still need to check
* that its not a minus number, otherwise we might end up owing the customer money!
*/
if(quant > 0)
{
drProduct["quantity"] = quant;
}
else
{
drProduct.Delete();
}
}
catch
{
// If we cant convert it to an Int then just leave it as it was i.e. make no changes
}
}
}
if(dtBasket.Rows.Count > 0)
{
// IF we still have items in the basket then update the Session Basket
Session["dtBasket"] = dtBasket;
}
else
{
// ELSE delete the Session Basket
Session.Contents.Remove("dtBasket");
}
// Refresh the basket display
updateShopBasket();
}
/// <summary>
/// Clear the entire basket
/// </summary>
protected void shopClearBasket_OnServerClick(object source, EventArgs e)
{
Session.Contents.Remove("dtBasket");
updateShopBasket();
}
/// <summary>
/// CHECKOUT
///
/// Pull all the basket data out of the Session and POST it to the external payment service
///
/// Doing it this way means that the address wont change for the first landing page of the service
///
/// This isnt usually a problem since they will have to login at which point the address will change
/// to match their actual location.
/// </summary>
protected void shopBasketCheckout_OnServerClick(object source, EventArgs e)
{
string postData = "";
postData += "currency_code=GBP";
postData += "&cmd=_cart";
postData += "&business=davidmillingtons@hotmail.com";
postData += "&upload=1";
postData += "&cancel_return=www.davidmillington.net";
DataTable dtBasket = getBasketDt();
double total = 0.00;
for(int i = 0; i < dtBasket.Rows.Count; i++)
{
postData += "&item_name_" + (i + 1) + "=" + dtBasket.Rows[i]["name"];
postData += "&quantity_" + (i + 1) + "=" + dtBasket.Rows[i]["quantity"];
postData += "&amount_" + (i + 1) + "=" + Convert.ToDouble(dtBasket.Rows[i]["price"]);
total += (Convert.ToDouble(dtBasket.Rows[i]["price"]) * Convert.ToInt32(dtBasket.Rows[i]["quantity"]));
if(i == dtBasket.Rows.Count - 1)
{
postData += "&shipping_" + (i + 1) + "=" + calcDeliveryCost(total);
}
else
{
postData += "&shipping_" + (i + 1) + "=0.00";
}
postData += "&shipping2_" + (i + 1) + "=0.00";
postData += "&handling_" + (i + 1) + "=0.00";
}
postData += "&handling=" + calcDeliveryCost(total);
// Response.Write("<!--" + postData + "-->");
byte[] data = Encoding.ASCII.GetBytes(postData);
HttpWebRequest ppRequest = (HttpWebRequest)WebRequest.Create("https://www.paypal.com/cgi-bin/webscr");;
ppRequest.Method = "POST";
ppRequest.ContentType = "application/x-www-form-urlencoded";
ppRequest.ContentLength = data.Length;
// Send
Stream ppStream = ppRequest.GetRequestStream();
ppStream.Write(data, 0, data.Length);
ppStream.Close();
// Receive :¬(
HttpWebResponse ppResponse = (HttpWebResponse)ppRequest.GetResponse();
StreamReader sr = new StreamReader(ppResponse.GetResponseStream());
string strResult = sr.ReadToEnd();
sr.Close();
// Write to screen
Response.Clear();
Response.Write(strResult);
Response.End();
}
} // EOC
} // EON
-------------------------------------------------------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation defaultLanguage="C#" debug="true" />
<customErrors mode="Off" />
<authorization>
<allow users="*" />
</authorization>
<trace
enabled="false"
requestLimit="10"
pageOutput="false"
traceMode="SortByTime"
localOnly="true"
/>
<globalization
requestEncoding="utf-8"
responseEncoding="utf-8"
/>
<httpRuntime
executionTimeout="90"
maxRequestLength="4096"
useFullyQualifiedRedirectUrl="false"
minFreeThreads="8"
minLocalRequestFreeThreads="4"
appRequestQueueLimit="100"
/>
</system.web>
<appSettings>
</appSettings>
</configuration>
No comments:
Post a Comment