65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (51投票s)

2010年7月14日

CPOL

7分钟阅读

viewsIcon

260646

downloadIcon

25737

一个简单易用的方法,可以为您的网站添加购物车并开始使用

shopping_basket/shoppingCard.jpg

您可以在 这里 试用我的在线演示。

目录

引言

我查阅了 CodeProject 和互联网上许多关于购物车的文章,但发现其中一些要么复杂,要么非常基础,要么功能有限,要么速度慢,要么需要您了解代码的每一个细节才能开始使用。

在我开发了这个用户控件之后,我觉得值得分享。因为大多数开发人员都忙于工作任务,不总是有时间了解某个特定技术的方方面面。如果有人能创建一个让您的生活更轻松的东西,那会快很多。

什么是购物车软件

购物车软件是电子商务中用于帮助人们在线购物的软件,类似于美式英语中的“shopping cart”。在英式英语中,它通常被称为 shopping basket,在网站上几乎都缩写为“basket”。

我为什么创建这个用户控件

我在互联网上搜索了一个具有简单、易用、在 Cookie 中保存已购商品、无页面闪烁、可移动、浮动且免费规格的购物车用户控件。但我没有找到任何这样的解决方案,所以我开发了一个用户控件来实现这些目标。

背景

要理解本文,您应该具备 ASP.NET、C#、JavaScript、Cookie 和用户控件的基础知识。

要运行此代码,您需要使用(.NET Framework 3.5 和 Visual Studio 2008)。

谁可能会感兴趣

我写这篇文章是为那些需要在其 Web 应用程序中添加购物车的人。

我的篮子用户控件的主要目标

  1. 从 Cookie 读取:这样,如果用户在未购买的情况下关闭浏览器,稍后返回时,他们仍然会在篮子中找到相同的商品,无需再次寻找这些商品。
  2. 浮动:这样它将始终显示在用户面前,他们可以知道自己购买了什么以及花了多少钱。
  3. 可移动:用户可以移动它并将其放置在浏览器中的任何位置。
  4. 可调整大小:用户可以显示和隐藏篮子。
  5. 无页面闪烁:这意味着可以在检索数据的同时工作。换句话说,只有当您添加或删除篮子中的产品时,用户控件的内容才会回发到服务器。
  6. 兼容主流浏览器,如 Firefox、Internet Explorer 和 Chrome。

如何将用户控件添加到您的 Web 应用程序

使用此控件应该很简单。

  1. 将(用户控件文件、JavaScript 文件、Images 文件夹)复制到您的 Visual Studio 2008 项目。
  2. 将用户控件拖放到您的 ASP.NET 页面中。(我建议将其放在页面末尾的 div 中,以避免任何设计影响。)
  3. 然后,您只需通过从 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

用户控件内部事件流程

  1. 将从 Web 页面添加一个新产品(通过将该产品添加到名为 ShoppingCart 的 Cookie 中)(客户端)。
    <a id="A1" href="javascript:addCookie('ShoppingCart',5,'Iphone4',500.00)">add
  2. 用户控件将从名为 ShoppingCart 的 Cookie 中读取选定的产品(服务器端)。
  3. 用户控件会将 Cookie 值绑定到数据表 dt_final(服务器端)。
  4. 用户控件会将数据表值视图显示到 Repeater 控件 rptShoppingCart(服务器端)。
  5. 已购商品的总金额将通过 Repeater 事件 ItemDataBound 计算(服务器端)。

深入了解用户控件源代码

  • 从 Cookie 读取已购商品

    我在用户控件的 page_load 事件中通过 Cookie ShoppingCart 读取已购商品。

    // 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;"> 

    shopping_basket/movable.gif

    // 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.jpgmin.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" />

    shopping_basket/MinMax1.gif

    // 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 控件,以便任何想尝试的人都能轻松上手。但请注意,在实际场景中,它应该从数据库读取。

    shopping_basket/Shopping_Card_Sample.jpg

最后

这是我在 CodeProject 上的第一篇文章。我希望它对许多读者有用。我花费了大量时间和精力来开发这个用户控件。请随时评论或提出建议。

浏览器兼容性

此用户控件已在 Firefox 3.6、Internet Explorer 8 和 Chrome 5 上进行了测试。

参考文献

修订历史

  • 2010 年 7 月 14 日:原始文章
  • 2010 年 7 月 17 日:对文章进行了格式化,使其更好,并根据读者的建议添加了更多解释
  • 2010 年 7 月 21 日:添加了尝试 在线演示 的链接
  • 2010 年 7 月 30 日:修复了用户删除所有商品然后再次添加新商品时的错误
  • 2011 年 5 月 25 日:更新了示例应用程序和源代码
© . All rights reserved.