简单购物车(篮子)用户控件,使用 ASP.NET & JavaScript(浮动、可移动、可调整大小)






4.84/5 (51投票s)
一个简单易用的方法,可以为您的网站添加购物车并开始使用
您可以在 这里 试用我的在线演示。
目录
- 引言
- 什么是购物车软件
- 我为什么创建这个用户控件
- 背景
- 谁可能会感兴趣
- 我的篮子用户控件的主要目标
- 如何将用户控件添加到您的 Web 应用程序
- 用户控件内部事件流程
- 深入了解用户控件源代码
- 值得思考的点
- 关注点
- 最后
- 参考文献
- 修订历史
引言
我查阅了 CodeProject 和互联网上许多关于购物车的文章,但发现其中一些要么复杂,要么非常基础,要么功能有限,要么速度慢,要么需要您了解代码的每一个细节才能开始使用。
在我开发了这个用户控件之后,我觉得值得分享。因为大多数开发人员都忙于工作任务,不总是有时间了解某个特定技术的方方面面。如果有人能创建一个让您的生活更轻松的东西,那会快很多。
什么是购物车软件
购物车软件是电子商务中用于帮助人们在线购物的软件,类似于美式英语中的“shopping cart”。在英式英语中,它通常被称为 shopping basket,在网站上几乎都缩写为“basket”。
我为什么创建这个用户控件
我在互联网上搜索了一个具有简单、易用、在 Cookie 中保存已购商品、无页面闪烁、可移动、浮动且免费规格的购物车用户控件。但我没有找到任何这样的解决方案,所以我开发了一个用户控件来实现这些目标。
背景
要理解本文,您应该具备 ASP.NET、C#、JavaScript、Cookie 和用户控件的基础知识。
要运行此代码,您需要使用(.NET Framework 3.5 和 Visual Studio 2008)。
谁可能会感兴趣
我写这篇文章是为那些需要在其 Web 应用程序中添加购物车的人。
我的篮子用户控件的主要目标
- 从 Cookie 读取:这样,如果用户在未购买的情况下关闭浏览器,稍后返回时,他们仍然会在篮子中找到相同的商品,无需再次寻找这些商品。
- 浮动:这样它将始终显示在用户面前,他们可以知道自己购买了什么以及花了多少钱。
- 可移动:用户可以移动它并将其放置在浏览器中的任何位置。
- 可调整大小:用户可以显示和隐藏篮子。
- 无页面闪烁:这意味着可以在检索数据的同时工作。换句话说,只有当您添加或删除篮子中的产品时,用户控件的内容才会回发到服务器。
- 兼容主流浏览器,如 Firefox、Internet Explorer 和 Chrome。
如何将用户控件添加到您的 Web 应用程序
使用此控件应该很简单。
- 将(用户控件文件、JavaScript 文件、Images 文件夹)复制到您的 Visual Studio 2008 项目。
- 将用户控件拖放到您的 ASP.NET 页面中。(我建议将其放在页面末尾的
div
中,以避免任何设计影响。) - 然后,您只需通过从 Web 页面调用名为
addcookie
的 JavaScript 函数来添加产品。
控件本身将处理修改数量、删除产品和提交请求。
以下代码说明了如何在您的 aspx 页面上使用该控件。
<!-- Drop the user control inside div at the end of your aspx page -->
<div>
<uc1:basket id="Basket1" runat="server" />
</div>
<!-- Note that when you drag and drop the user control to your web page the
registration tag will be added automatically to the top of the page. -->
<%@ Register src="Basket.ascx" tagname="Basket" tagprefix="uc1" %>
<!-- then at any place in your aspx page you can call the JavaScript function
to add a new product to the basket
addcookie('ShoppingCart',product_id,'product name',price) -->
i.e. <a id="A1" href="javascript:addCookie('ShoppingCart',5,'Iphone4',500.00)">add
用户控件内部事件流程
- 将从 Web 页面添加一个新产品(通过将该产品添加到名为
ShoppingCart
的 Cookie 中)(客户端)。<a id="A1" href="javascript:addCookie('ShoppingCart',5,'Iphone4',500.00)">add
- 用户控件将从名为
ShoppingCart
的 Cookie 中读取选定的产品(服务器端)。 - 用户控件会将 Cookie 值绑定到数据表
dt_final
(服务器端)。 - 用户控件会将数据表值视图显示到 Repeater 控件
rptShoppingCart
(服务器端)。 - 已购商品的总金额将通过 Repeater 事件
ItemDataBound
计算(服务器端)。
深入了解用户控件源代码
-
从 Cookie 读取已购商品
我在用户控件的
page_load
事件中通过 CookieShoppingCart
读取已购商品。// The below code reads the purchased items from cookies // ShoppingCart and puts it in data table dt_final, then // binds it to a data repeater control rptShoppingCart protected void Page_Load(object sender, EventArgs e) { // To Make sure the cookie is not empty if (Request.Cookies["ShoppingCart"] != null) { HttpCookie oCookie = (HttpCookie)Request.Cookies["ShoppingCart"]; string sProductID = oCookie.Value.ToString(); DataTable dt_final = new DataTable(); // which means the user remove all products and he is adding a new product // in this case i need to remove the ",". // otherwise the '' will be considered as item. if (sProductID.IndexOf(",") == 0) { sProductID=sProductID.Remove ( 0, 1); } if (sProductID != "") { char[] sep = { ',' }; // split the cookie values into array string[] sArrProdID = sProductID.Split(sep); //create datatable for purchased items 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"))); // to map the values from array of string(sArrProdID) to datatable 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++; } //temp table to return the distinct values only DataTable dtTemp = new DataTable(); string[] col = { "ProductID", "ProductName", "prod_price" }; dtTemp = dt.DefaultView.ToTable(true, col); dt_final = dt.Clone(); //to calculate the number of issued items 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++; } } // to bind the datatable to data repeater control rptShoppingCart rptShoppingCart.DataSource = dt_final; rptShoppingCart.DataBind(); } }
-
计算总金额
为了计算已购商品的总金额,我使用了 Repeater 事件
ItemDataBound
。
每次将数据记录添加到 Repeater 控件时,都会触发ItemDataBound
事件。在该事件中,您可以访问绑定到该行的数据。此功能使您能够计算已购产品的总和。//Page level variable to save the total price when //the event rptShoppingCart_ItemDataBound has been fired. decimal decPriceSum;
//To calculate the total money for purchased items protected void rptShoppingCart_ItemDataBound(object sender, RepeaterItemEventArgs e) { // To reset the counter incase of header if (e.Item.ItemType == ListItemType.Header) { decPriceSum = 0; } // to add the product price to the sum variable (decPriceSum) else if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { decPriceSum += (decimal)((DataRowView)e.Item.DataItem)["prod_price"]; } // to view the total incase of footer else if (e.Item.ItemType == ListItemType.Footer) { Label lblSum = e.Item.FindControl("lblItemPrice") as Label; lblSum.Text = "Total Price: $ " + decPriceSum; } }
-
将商品添加到篮子
要将产品添加到篮子,当用户单击“添加”链接时,我首先调用 JavaScript 函数
addCookie
。
然后我强制回发以重新加载篮子项,方法是调用 JavaScript 函数__doPostBack
。// To add a new product to the cookie function addCookie(name, value, prod_name, prod_price) { var today = new Date(); //to set the cookies expiry time var expires = expires * 1000 * 3600 * 3; //To retrieve the values of cookie named "ShoppingCart" var currentCookie = getCookie(name); if (currentCookie == null) { //it means this is the first item in the basket document.cookie = name + '=' + escape(value) + "," + escape(prod_name) + "," + escape(prod_price) + ((expires) ? ';expires=' + new Date(today.getTime() + expires).toGMTString() : ''); } else { //it means the basket already has another products document.cookie = name + '=' + currentCookie + "," + escape(value) + "," + escape(prod_name) + "," + escape(prod_price) + ((expires) ? ';expires=' + new Date(today.getTime() + expires).toGMTString() : ''); } //To maximize the basket size in case it was minimized by the user showdiv("Basket_body") //to force the post back to reload the basket items after adding product __doPostBack('Basket1_UpdatePanel1', ''); } // To retrieve the basket cookie values 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)); }
-
从篮子中移除商品
要从篮子中移除产品,当用户单击“移除”链接时,我首先调用 JavaScript 函数
deleteCookie
。
然后我强制回发以重新加载篮子项,方法是调用 JavaScript 函数__doPostBack
。//To remove the product from cookies when the user press on remove link function deleteCookie(name, value,prod_name,prod_price) { //to set the cookie expiry time var expires = expires * 1000 * 3600 * 3; //In case of the removed item in the mid of the cookie 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() : ''); } //In case of the removed item is the first item in cookie 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() : ''); } //In case of the removed item is the only item in cookie 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() : ''); } //to force the post back to reload the basket items after removing product __doPostBack('Basket1_UpdatePanel1', ''); }
// This Javascript function will force a postback after // removing a product to reload the basket items. function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } }
-
可移动/浮动的用户控件
为了使用户控件可移动,当用户拖放篮子标题时,我在篮子标题 DIV
Header_Div
上添加了三个客户端事件,如下所示:onmousedown="begindrag(event)"
当用户按下鼠标左键(开始拖动)onmousemove= "mousepos(event)"
当用户在按下鼠标左键时移动鼠标(拖动中)onmouseup="rel_drag(event)"
当用户释放鼠标时(放置)
<div id="Header_Div" onmousedown="begindrag(event)" onmouseup="rel_drag(event)" onmousemove= "mousepos(event)" style="cursor:hand;">
// Below is the JavaScript code for Div drag and drop var drag = 0; // a Boolean variable to determine the drag status var xdif = 0; // a variable to save the new X position for the basket var ydif = 0; // a variable to save the new Y position for the basket 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); } } // to move the basket during drag (mouse down and move) function mousepos(event) { floatingd = document.getElementById("whole_basket"); // to check that the mouse still down if (drag == 1) { floatingd.style.left = Math.abs(event.clientX - xdif) + "px"; floatingd.style.top = Math.abs(event.clientY - ydif) + "px"; ; } } //when mouse up to release the basket function rel_drag(event) { drag = 0; } // End drag and drop code
-
可调整大小的用户控件
为了使用户控件可调整大小,我在篮子标题的同一位置放置了两个图像(max.jpg 和 min.jpg)。
我根据用户的点击在这两个图像之间切换。一旦用户单击最小化,我将隐藏最小化图像并将篮子大小更改为最小,然后显示最大化图像。当用户单击最大化时,我将隐藏最大化图像并将篮子大小更改为最大,然后显示最小化图像。
在最大化的情况下,我调用 JavaScript 函数showdiv('Basket_body')
;在最小化的情况下,我调用 JavaScript 函数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" />
// This is the JavaScript code to make the user control resizable. // //to Minimize the user control 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'; } // to maximize the user control 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'; } // End Resizable Code
-
嵌入式样式表
我在用户控件文件中使用了嵌入式样式表,因为我试图使其尽可能地封装。通过样式表,您可以控制篮子的默认位置、大小、颜色等。
<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 /* Basket Header Div Style */ { position:relative; height: 27px; vertical-align: middle; background-color: #EE3434; } .img_min_max /* Minimize and Maximize image style */ { left:3px; position:absolute; } .Basket_Header_Label /*The style of basket Header label*/ { top:5px; left:30px; position:absolute; color :White; Font-Size:small; } #Basket_body /* the style of basket content*/ { position:relative; font-size: 11px; height: 200px; visibility:visible; background-color: #FFFF99; } .Column_header /* Basket Column Header Product Name, Qty, Price */ { color:Black; font-weight:bold; } </style>
值得思考的点(为什么篮子不是完全客户端脚本?)
我尽最大努力使这个用户控件完全客户端化(无需回发到服务器)。但我失败了,因为我相信需要编写一些复杂的客户端代码脚本(例如将 Cookie 信息绑定到表并进行计算)。
因此,作为一种变通方法,我使该用户控件成为添加或删除产品时唯一回发到服务器的部分。用户将不会注意到 Web 页面的任何闪烁。
此外,您可以将其视为一种商业策略;因为有时您需要回发到服务器以确保产品仍然可用。
关注点
-
篮子已结账
一旦用户完成购买并单击“提交”,他将被重定向到一个名为 submit.aspx 的结账页面以填写其详细信息。
在您的结账页面中,您可以像在 上面 提到的那样,在用户控件的page_load
中读取已购商品。protected void btnSubmit_Click(object sender, EventArgs e) { // To redirect the user once he finished shopping and press submit button Response.Redirect("submit.aspx", false); }
-
固定位置的篮子
请注意,如果您想将篮子固定在某个位置(这是首选),您只需要从标题 DIV
Header_Div
中删除 JavaScript 函数(onmousedown="begindrag(event)"
onmousemove= "mousepos(event)"
onmouseup="rel_drag(event)"
)。 -
关于示例应用程序
在我的示例应用程序中,我从 XML 文件读取产品信息并将其绑定到
Gridview
控件,以便任何想尝试的人都能轻松上手。但请注意,在实际场景中,它应该从数据库读取。
最后
这是我在 CodeProject 上的第一篇文章。我希望它对许多读者有用。我花费了大量时间和精力来开发这个用户控件。请随时评论或提出建议。
浏览器兼容性
此用户控件已在 Firefox 3.6、Internet Explorer 8 和 Chrome 5 上进行了测试。
参考文献
修订历史
- 2010 年 7 月 14 日:原始文章
- 2010 年 7 月 17 日:对文章进行了格式化,使其更好,并根据读者的建议添加了更多解释
- 2010 年 7 月 21 日:添加了尝试 在线演示 的链接
- 2010 年 7 月 30 日:修复了用户删除所有商品然后再次添加新商品时的错误
- 2011 年 5 月 25 日:更新了示例应用程序和源代码