You can try my live demo here.
Contents
I went through many articles related to shopping cart on CodeProject and on the Internet, but I found that some of them are complex, or very basic, or have little functionality, or they are slow, or they need you to understand each and every thing to start using the code.
After I developed this user control, I feel that it could be useful to share. Because most of the developers are busy in their employment tasks, it's not always possible to know everything about a certain technology, just because there is little time. It goes a lot faster if someone creates something to make your life a little bit easier.
Shopping cart software is software used in e-commerce to assist people in making purchases online, analogous to the American English term 'shopping cart'. In British English, it is generally known as a shopping basket, almost exclusively shortened on websites to 'basket'.
I searched the internet for a shopping cart user control with a specification like simple, ease of use, saving purchased items in cookies, no pages flickering, movable, floating and free. But I haven't found any solution like this, so I developed a user control to achieve these goals.
To understand this article, you should have basic background about ASP.NET, C#, JavaScript, Cookies and User controls.
To run this code, you need to use (.NET Framework 3.5 and Visual Studio 2008).
I have written this article for those who need to have a shopping basket in their web applications.
- Reading from cookies: So if users quit their browser without making a purchase and return later, they still find the same items in the basket so they do not have to look for these items again.
- Floating: So it will be in front of users all the time and they can know what they purchased and how much it cost.
- Movable: So the Users can move it and put it at any place in the browser.
- Resizable: So the users can show and hide the Basket.
- No page flickering: which means the possibility of working while the data is retrieved. In other words, the user control contents only will be posted back to the server when you add or remove products from the basket.
- Compatible with major browsers like Firefox, Internet Explorer and Chrome.
Using this control should be straightforward.
- Copy (User control file, JavaScript File, Images folder) to your Visual Studio 2008 project.
- Drag and drop the user control into your ASP.NET page.(I recommend to put it inside
div
at the end of your page to avoid any design effect.) - Then you need only to add products through calling a JavaScript function named
addcookie
from your web page.
The control itself will handle modifying quantities, removing products and submit the request.
The below code illustrates how you can use the control on your aspx page.
<div>
<uc1:basket id="Basket1" runat="server" />
</div>
<%@ Register src="Basket.ascx" tagname="Basket" tagprefix="uc1" %>
i.e. <a id="A1" href="javascript:addCookie('ShoppingCart',5,'Iphone4',500.00)">add
- A new product will be added from Web page (by adding this product to cookie named
ShoppingCart
) (client - side).
<a id="A1" href="javascript:addCookie('ShoppingCart',5,'Iphone4',500.00)">add
- The user control will read the selected products from a cookie named
ShoppingCart
(server - side). - The user control will bind cookie values to data table
dt_final
(server - side). - The user control will view the data table values into Repeater control
rptShoppingCart
(server - side). - The total amount of purchased items will be calculated through repeater event
ItemDataBound
(server - side).
-
I am reading the purchased items from cookies ShoppingCart
through the user control page_load
event.
protected void Page_Load(object sender, EventArgs e)
{
if (Request.Cookies["ShoppingCart"] != null)
{
HttpCookie oCookie = (HttpCookie)Request.Cookies["ShoppingCart"];
string sProductID = oCookie.Value.ToString();
DataTable dt_final = new DataTable();
if (sProductID.IndexOf(",") == 0)
{
sProductID=sProductID.Remove ( 0, 1);
}
if (sProductID != "")
{
char[] sep = { ',' };
string[] sArrProdID = sProductID.Split(sep);
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Counter"));
dt.Columns.Add(new DataColumn("ProductID"));
dt.Columns.Add(new DataColumn("ProductName"));
dt.Columns.Add(new DataColumn("Issue"));
dt.Columns.Add(new DataColumn(("prod_price"),
System.Type.GetType("System.Decimal")));
int counter = 1;
for (int i = 0; i < sArrProdID.Length - 1; i = i + 3)
{
DataRow dr = dt.NewRow();
dr["Counter"] = counter;
dr["ProductID"] = sArrProdID[i];
dr["ProductName"] = sArrProdID[i + 1];
dr["Issue"] = 1;
dr["prod_price"] = sArrProdID[i + 2];
dt.Rows.Add(dr);
counter++;
}
DataTable dtTemp = new DataTable();
string[] col = { "ProductID", "ProductName", "prod_price" };
dtTemp = dt.DefaultView.ToTable(true, col);
dt_final = dt.Clone();
counter = 1;
foreach (DataRow dr in dtTemp.Rows)
{
DataRow dr_final = dt_final.NewRow();
dr_final["ProductID"] = dr["ProductID"];
dr_final["ProductName"] = dr["ProductName"];
dr_final["Issue"] = dt.Compute("count(ProductID)",
"ProductID='" + dr["ProductID"] + "'").ToString();
dr_final["Counter"] = counter;
dr_final["prod_price"] = dt.Compute("sum(prod_price)",
"ProductID='" + dr["ProductID"] + "'");
dt_final.Rows.Add(dr_final);
counter++;
}
}
rptShoppingCart.DataSource = dt_final;
rptShoppingCart.DataBind();
}
}
-
To calculate the total money for purchased items, I have used the repeater event ItemDataBound
.
Each time a data record is added to the Repeater control, an ItemDataBound
event is fired. Within the event, you can access the data being bound to the row. This feature enables you to calculate the sum for purchased products.
decimal decPriceSum;
protected void rptShoppingCart_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Header)
{
decPriceSum = 0;
}
else if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
ListItemType.AlternatingItem)
{
decPriceSum +=
(decimal)((DataRowView)e.Item.DataItem)["prod_price"];
}
else if (e.Item.ItemType == ListItemType.Footer)
{
Label lblSum = e.Item.FindControl("lblItemPrice") as Label;
lblSum.Text = "Total Price: $ " + decPriceSum;
}
}
-
To add a product to the basket when the user presses on add link, first I call a JavaScript function addCookie
.
Then I force postback to reload the basket items by calling the JavaScript function __doPostBack
.
function addCookie(name, value, prod_name, prod_price) {
var today = new Date();
var expires = expires * 1000 * 3600 * 3;
var currentCookie = getCookie(name);
if (currentCookie == null) {
document.cookie = name + '=' + escape(value) + "," +
escape(prod_name) + "," + escape(prod_price) +
((expires) ? ';expires=' + new Date(today.getTime() +
expires).toGMTString() : '');
}
else {
document.cookie = name + '=' + currentCookie + "," +
escape(value) + "," + escape(prod_name) + "," +
escape(prod_price) + ((expires) ? ';expires=' +
new Date(today.getTime() + expires).toGMTString() : '');
}
showdiv("Basket_body")
__doPostBack('Basket1_UpdatePanel1', '');
}
function getCookie(name) {
var sPos = document.cookie.indexOf(name + "=");
var len = sPos + name.length + 1;
if ((!sPos) && (name != document.cookie.substring(0, name.length))) {
return null;
}
if (sPos == -1) {
return null;
}
var ePos = document.cookie.indexOf('=', len);
if (ePos == -1) ePos = document.cookie.length;
return unescape(document.cookie.substring(len, ePos));
}
-
To remove a product from the basket when the user presses on remove link, first I call a JavaScript function deleteCookie
.
Then I force postback to reload the basket items by calling the JavaScript function __doPostBack
.
function deleteCookie(name, value,prod_name,prod_price) {
var expires = expires * 1000 * 3600 * 3;
if (document.cookie.indexOf("," + value + "," +
prod_name + "," + prod_price) != -1)
{
document.cookie = document.cookie.replace("," +
value + "," + prod_name + "," + prod_price, "") +
((expires) ? ';expires=' + new Date(today.getTime() +
expires).toGMTString() : '');
}
else if (document.cookie.indexOf(value + "," +
prod_name + "," + prod_price + ",") != -1)
{
document.cookie = document.cookie.replace
(value + "," + prod_name + "," + prod_price + "," , "") +
((expires) ? ';expires=' + new Date(today.getTime() +
expires).toGMTString() : '');
}
else if (document.cookie.indexOf(value + "," +
prod_name + "," + prod_price ) != -1)
{
document.cookie = document.cookie.replace
(value + "," + prod_name + "," + prod_price, "") +
((expires) ? ';expires=' + new Date(today.getTime() +
expires).toGMTString() : '');
}
__doPostBack('Basket1_UpdatePanel1', '');
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
-
To make the user control movable when the user drags and drops the basket header, I added three client side events to the basket header DIV Header_Div
as below:
onmousedown="begindrag(event)"
when the user clicks mouse down (Start dragging) onmousemove= "mousepos(event)"
when the user moves the mouse while he is clicking mouse down (Dragging) onmouseup="rel_drag(event)"
when the user releases the mouse (Drop)
<div id="Header_Div" onmousedown="begindrag(event)"
onmouseup="rel_drag(event)" onmousemove= "mousepos(event)" style="cursor:hand;">
var drag = 0;
var xdif = 0;
var ydif = 0;
function begindrag(event) {
if (drag == 0) {
floatingd = document.getElementById("whole_basket");
prex = floatingd.style.left.replace(/px/, "");
prey = floatingd.style.top.replace(/px/, "");
drag = 1;
xdif = Math.abs(event.clientX - prex);
ydif = Math.abs(event.clientY - prey);
}
}
function mousepos(event) {
floatingd = document.getElementById("whole_basket");
if (drag == 1) {
floatingd.style.left = Math.abs(event.clientX - xdif) + "px";
floatingd.style.top = Math.abs(event.clientY - ydif) + "px"; ;
}
}
function rel_drag(event) {
drag = 0;
}
-
To make the user control resizable, I put two images (max.jpg and min.jpg) in the same place at basket header.
I toggle between them according to the user click. Once the user click minimizes, I hide the minimize image and change the basket size to minimum, then show the maximize image. And when the user clicks maximize, I hide the maximize image and change the basket size to maximum then show the minimize image.
In case of Maximize, I call the JavaScript function showdiv('Basket_body')
and in case of Minimize, I call JavaScript function hidediv('Basket_body')
.
<img id="imgShow" alt="" src="Images/min.jpg"
onclick="javascript:showdiv('Basket_body')" class="img_min_max" />
<img id="imgHide" alt="" src="Images/max.jpg"
onclick="javascript:hidediv('Basket_body')" class="img_min_max" />
function hidediv(id) {
document.getElementById(id).style.display = 'none';
document.getElementById("whole_basket").style.height = '46px';
document.getElementById("whole_basket").style.width = '130px';
document.getElementById("imgShow").style.display = 'block';
document.getElementById("imgHide").style.display = 'none';
}
function showdiv(id) {
document.getElementById(id).style.display = 'block';
document.getElementById("whole_basket").style.height = '255px';
document.getElementById("whole_basket").style.width = '270px';
document.getElementById("imgShow").style.display = 'none';
document.getElementById("imgHide").style.display = 'block';
}
-
I have used an embedded style sheet inside user control file because I tried to make it encapsulated as much as I can. From the style sheet, you can control the default position for your basket, size, color, etc.
<style type="text/css">
#whole_basket
{
left: 650px;
top: 200px;
position:fixed;
width: 270px;
border-style:solid;
border-width:1px;
border-color:Red;
overflow:auto;
background-color: #FFFF99;
}
#Header_Div
{
position:relative;
height: 27px;
vertical-align: middle;
background-color: #EE3434;
}
.img_min_max
{
left:3px;
position:absolute;
}
.Basket_Header_Label
{
top:5px;
left:30px;
position:absolute;
color :White;
Font-Size:small;
}
#Basket_body
{
position:relative;
font-size: 11px;
height: 200px;
visibility:visible;
background-color: #FFFF99;
}
.Column_header
{
color:Black;
font-weight:bold;
}
</style>
I tried my best to make this user control fully client side (no post back to the server). But I failed, because as I believe it is needed to write some complex client code script. (like binding the cookie info to a table and doing the calculations).
So as a work around, I made the user control to be the only portion posted back to the server in case of adding or deleting products. And the user will not notice any flickering in the web page.
Moreover, you can consider this as business wise; because sometimes you need to post pack to the server to make sure that the products are still available.
-
Once the user finishes purchasing and presses "Submit", he will be redirected to a checked out page named submit.aspx to fill his details.
In your checked out page, you can read the purchased items in the same way as you are reading from basket page_load
as mentioned above.
protected void btnSubmit_Click(object sender, EventArgs e)
{
Response.Redirect("submit.aspx", false);
}
-
Note that if you want to keep the basket in fixed place which is preferred, you have to remove only the JavaScript functions (onmousedown="begindrag(event)"
onmousemove= "mousepos(event)"
onmouseup="rel_drag(event)"
) from the header DIV Header_Div
.
-
In my sample application, I read the product info from XML file and bind it to Gridview
control to make it easy for anyone wanting to try it. But note that in real life scenario, it should be read from the database.
This is my first article on CodeProject. I hope it will be useful for many readers. I spent a lot of time and effort to develop this user control. Please feel free to comment or advice.
Browser Compatibility
This user control has been tested on Firefox 3.6, Internet Explorer 8 and Chrome 5.
- 14 Jul 2010: Original article
- 17 Jul 2010: Formatted the article to be better and added more explanation based on readers' advice
- 21 Jul 2010: Added link to try live demo
- 30 Jul 2010: Fixed bug when the user removed all items from the basket and again added a new item
- 25 May 2011: Updated sample application and source code