You can try my live demo here.
Contents
I went through many articles related shopping cart inside Codeproject and in the Internet but I found that some of them are complex, or very basic, or little functionality, or it is slow, or it or need you to understand each and everything to start using the code.
So 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 of a certain technology, just because there is little time. It goes a lot faster if someone create something to make your life a little bit easier.
Shopping cart software is software used in e-commerce to assist people 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 a 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 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 post backed to the server when you add or remove products from the basket.
- Compatible with major browsers like Firefox, IE 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 press on add link. First I am calling a JavaScript function
addCookie.
Then I am forcing 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 press on remove link. First I am calling a JavaScript function
deleteCookie.
Then I am forcing 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 drag and drop the basket header,
I added three client side events to the basket header DIV
Header_Div as below:
onmousedown="begindrag(event)" when the user click mouse down (Start Dragging)
onmousemove= "mousepos(event)" when the user move the mouse while he is clicking mouse down (Dragging)
onmouseup="rel_drag(event)" when the user release 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 am toggling between them according to the user click. Once the user click minimize, I am hiding the
minimize image and changing the basket size to minimum then showing the maximize image. And when the user click maximize, I am hiding the maximize image and change the basket size to maximum
then showing the minimize image.
Incase of Maximize I am calling JavaScript function
showdiv('Basket_body') and Incase of Minimize I am calling 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; /* Default Left Position for the basket */
top: 200px; /* Default Top Position for the basket */
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 need to write some complex client code script.(Like bind the cookie Info to a table and do 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.
This is my first article in 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 Firefox3.6, Internet Explorer8 and Chrome5.
- 14 Jul 2010: Original article.
- 17 Jul 2010: Formatted the article to be better and I added more explanation based on readers advice..
- 21 Jul 2010: I added link to try live demo.
- 30 Jul 2010: Fixing Bug when the user remove all items from the basket and again add a new item.