通用购物篮

分享于 

16分钟阅读

Web开发

  繁體

介绍

网络似乎充斥着不同的购物篮和( PHP,ASP,jscript。)的不同方式来解决这个问题。 我觉得写一个通用的购物篮作为一个好的独立网页控件是很有趣的。 整个想法是制作一个包含尽可以能多的"购物篮逻辑"的web控件,以便在现有或者新 ASP.NET 项目中包含最少的编码。 一个目标是保持代码相对较轻,以便控件可以作为一种在 C# 中开发控件的介绍,甚至可以作为对一些非常常见的控件的教程的介绍。 NET和 C# 技术。 结果显得非常好,我确信即使开始的.NET 程序员也能够理解所有的代码。 当然,有很多方法可以改变和改进web控件中的一些概念。 我将提到相关部分的可以能改进,以便你可以使用这里控件作为模板添加更适合你的需求。 对于web控件,你可以做很多额外的操作,但是它的中的一些超出了文章的范围。 那么我们开始购物吧。

背景

当我设计控件时,我必须做出一些决定,因为"泛型"当然意味着某种开放设计。 首先,我不希望我的控件使用 Viewstate 传递设置,因为我希望一个设计能够让网页上的。 现在有几种方法可以做到这一点,但是在会话状态中保存篮看起来像最好的通用解决方案。 请注意,.NET 中的会话状态不再是那么邪恶,因为它是在普通的ASP中。 会话状态现在可以很容易地用于群集网络环境。

.NET 允许我用二进制格式或者XML格式来序列化类。 因为它将使开发人员能够从会话状态( 当然,你也可以在 ASP.NET 页面上使用控件时使用该控件) 读取篮内容,所以我决定使用XML方法。 我没有使用序列化,因为它有麻烦处理 private 成员,我更喜欢在时间内滚动自己的序列化。 注意,如果希望使篮会话状态更小,则可以实现二进制序列化。

第二大决定是使用复合控件( 包含其他子. NET 控件的控件的控件),但仍然处理渲染的大部分内容。 这个结果可以更快地控制( 根据 MS ),但是我确实做到了,因为我可以看到控件是如何被控件吐出的。 当然,这也使得控件的外观和感觉变得更容易适应。 如果你不喜欢它的外观,只需更改 Render 代码。 主机控制逻辑使捕捉事件( 使用 OnBubbleEvent ) 更容易,INamingContainer 接口确保子级具有唯一的,。

使用代码

使用这个控件应该非常简单。 只需将它作为网页控件添加到VS2003工具栏上,然后放到 ASP.NET 网页上。 你应该在网页上提供逻辑来添加产品,因为网络控件不能知道这些产品的位置。 你需要记住的主要事情是控制本身控制修改数量,移除产品和清空篮子。 另外,控件提供了一个处理程序,当用户按下篮子的Checkout按钮时可以使用。 当设置为"已经禁用"时,这个篮子没有显示任何按钮,所以这个状态只能显示篮子的内容( 在结帐页上,例如)。 这个篮子保存了它的所有信息,所以你可以在网站上的任何网页上使用篮子。 下面是演示如何在 aspx.cs 页面上使用控件的示例代码。

protected GenericBasket.Basket Basket1;// Events will be added in design mode or you can do it manually// Example : this.Basket1.Checkout += new System.EventHandler(// this.Page_Checkout);privatevoid Page_Checkout(object sender, System.EventArgs e)
{
 // disable the basket so that no buttons can be pressed Basket1.Enabled = false;
 // do some other stuff to checkout// add an item that includes the Shipping & Handling fee// I just add 10% of the total cost for demo purposes Basket1.Remove("SHIPPING");
 Basket1.Add("SHIPPING","+ Shipping & Handling",1,Basket1.TotalPrice/10);
}privatevoid Page_Load(object sender, System.EventArgs e)
{
 if (!IsPostBack)
 {
 // Add some products Basket1.Add("1","XBox",2,199);
 Basket1.Add("2","GameCube",1,99);
 Basket1.Add("3","PS2",1,199);
 Basket1.Add("4","Remove me",1,0);
 // and remove a product again Basket1.Remove("4");
 // of course you can also use the product class GenericBasket.Product newProduct = new GenericBasket.Product(
 "4","GameBoy Advance",1,140);
 Basket1.Add(newProduct );
 }
 }
}

GenericBasket.Product

public ( 例如 )的方法和属性。 你可以在 ASP.NET 页上使用的内容已经被保留到最低限度。 但是,Product 类是 public,所以你可以将它作为你的产品操作的包装器使用。 我保持了 Product 类非常基本,因为有很多不同的属性,你可以附加到产品中。 控件的设计假设你将自己获得产品特定的信息( 税地点库存。),并在签出周期中做一些事情。

当然,如果你希望购物篮包含更多的业务逻辑,你可以轻松地向 Product 类添加一些特定的内容。 Product 类实现 ICompareable 接口。 这里接口提供排序以便我们可以轻松地对 Product 元素的array 排序。

把信息从篮子里拿出来

篮子支持( 只读) 索引器和枚举器,因此它的行为非常像普通的array 和/或者集合。 下面的代码是非常有效的:

Response.Write(Basket1.Count.ToString());
Response.Write(" Items in Basket");foreach (GenericBasket.Product itemProduct in Basket1)
{
 Reponse.Write("*");
 Response.Write(itemProduct.sName);
 Response.Write("");
}// Get the product with ID"9"if (Basket1["9"]!= null)
 Response.Write("Product ID 9 found");else Response.Write("Product ID 9 not found");

实现这些非常简单。 枚举器不需要大量代码,基本上你可以从 ArrayList 成员返回现有的枚举数。 不要忘记添加 IEnumerable 接口来支持枚举器。 同样,支持索引器也很简单,只有一些简单的代码支持整数索引和一些. NET 类。 下面是一个索引器的代码,字符串版本非常相似:

public Product this [int index] // Indexer declaration{
 get {
 if (index >= 0 && index < m_aProducts.Count)
 return(m_aProducts[index] as Product);
 elsereturn(null);
 }
}

事件

对于事件冒泡,控件可以确保某些事件"气泡"向上到父控件。 在我们的一般篮子的情况下,我决定只将两个事件打泡到顶部。 为了支持事件冒泡,你需要实现 INamingContainer 接口。

publicevent EventHandler Checkout; // Event that is triggered// when checkout button is pressedpublicevent EventHandler Changed; // Event that happens when// control changes *itself*

若要正确处理由控件激发的所有命令,需要将 CommandArgument 属性指定给所创建的所有子控件。 例如在 Button 上:

btnGeneric = new Button();
btnGeneric.Text = "Delete";
btnGeneric.CommandName = "Delete";
btnGeneric.CommandArgument = outputProduct.sID;
btnGeneric.CssClass = CssClass;
Controls.Add(btnGeneric);

这样,就会产生一个冒泡事件处理方法,如下所示:

protectedoverridebool OnBubbleEvent(object source, EventArgs e) 
{ 
 bool handled = false;
 if (e is CommandEventArgs)
 {
 CommandEventArgs ce = (CommandEventArgs)e;
 if (ce.CommandName == "Delete")
 {
 Remove((string)ce.CommandArgument); 
 OnChanged(ce);
 handled = true; 
 }
 }
 return(handled); 
}protectedvirtualvoid OnChanged (EventArgs e)
{
 if (Changed!= null)
 {
 Changed(this,e);
 }
}

复合控件?

我选择进行复合控制( 与我自己呈现所有HTML元素并处理 postback 结果的控件相对应)的方式,因为我认为. NET 处理从子级激发的事件很好。 当然,复合控件稍微慢一些,并且存在一些小缺陷。 如果你真正遇到性能问题,你可能需要完全重新设计控件,以便它能够处理自己的呈现和支持 postback 数据处理( 实现 IPostBackDataHandler )。

我遇到了一个严重的问题。 显然. NET 框架在处理子控件触发的事件之前调用 CreateChildControls。 这导致了非常奇怪的行为和包含错误信息的错误控制。 这个解决方案是相当简单的( 但并不令人满意)。 我只是在 PreRender 事件中添加了 CreateChildControls 调用。 为什么不满意如果你使用调试器进行了代码,你将看到 CreateChildControls 现在被调用两次。 不是很敏感,是吧? 我已经留下这个了,因为我觉得有一种更优雅的方法来解决这个问题。

公用 方法

下面是你可以在篮子中使用的方法的List 和简短描述:

public System.Int32 Add ( GenericBasket.Product newProduct ); 
 // Add a new Product to the basket. Returns index at which // the value has been added.public System.Int32 Add ( System.String sProductID, 
 System.String sName, System.Int32 iQuantity, System.Single fPrice); 
 // Add a new product to the basket alternative version. // Returns index at which the value has been added.public Basket ( ); // Constructor, nothing interesting to //say about this one ;)publicvoid Clear ( ); // Clears the content of the basketpublicint Count [ get]; // The number of items in the basketpublicstring EmptyTitle [ get, set ]; // The title of control // when it is emptypublic System.Collections.IEnumerator GetEnumerator ( ); 
 // returns an enumerator, used for implementing enumerationpublicvoid Remove ( System.String sProductID ); // Removes a product // from the basket based on product IDpublicstring SessionKey [ get]; // The"session key" that you // can use to read the basket session information XMLpublicconst GenericBasket.Product this [ get]; // Indexers, // one takes int index and the other one takes a product ID stringpublicstring Title [ get, set ]; // The title of your basket // (when there are items inside)// New in version 1.1publicfloat TotalPrice [ get]; // Gets the total price of all items in basketpublic System.Globalization.CultureInfo Culture [ get, set ]; 
 // the Culture of the basket, this makes sure the // basket can display price in correct format

Points of Interest

我必须添加这是我对CodeProject的第一个. NET 贡献。 我把 switch。 NET大约一年前从 C++ 开始,并迅速发现。 NET框架是一个巨大的迷宫,具有不同的路径来。 在编写这个控件时,我经常发现不同的方法,在开始修改这个控件之前,应该考虑一些新的更好的方法来改进设计过程。

  • 你可以使用更高级的子控件( DataGrid,例如) 来显示篮子的内容。
  • 你甚至可以将XML会话数据绑定到 DataGrid。 这将给你一个很好的外观和一些不错的DataGrid 功能。
  • 添加数据库保存/加载也很好,因为你的网站访问者可以能希望它们的篮子在访问之间。 这可以在控件中实现,也可以作为事件公开给父( 就像结帐一样)。
  • Product 类非常基本,可以扩展。 产品页面的URL,或者是图片的链接?
  • 可以合并一些业务逻辑。 例如自动添加税收和/或者运费成本。
  • 现在有很多可以完成的布局,现在很基本。 修改样式样式当然是非常可以行的,也许你可以向 Render 方法中的一些HTML元素添加控件的UI。 记住,控件目前将 CssClass 属性传播到所有的HTML元素。 这给你一些布局控制,但可能需要更多的。

如果你决定使用购物篮,我希望这篇文章给你一些想法和有用的代码。 如果有人决定改进设计,让我知道,以便我可以在文章的较新版本中进行合并。 have购物 !

历史记录

  • 版本 1.1:
    • 修正了索引器中的一个小 Bug。
    • 附加价格信息。
    • 添加了文化信息,使篮子知道如何显示价格信息。
    • 更改了一些变量名称以使它们更符合".NET 规范"。
  • 1.2 版
    • 修正了当篮子为空时,HTML输出中的一个小的Bug ( 最后一个 TD 标记未关闭)。
    • 在 table 元素之间添加了一些"& nbsp ;",使 table 看起来一致。
    • 添加 [Browsable(false) ] 对于某些不应真正出现在属性窗口中的属性。
    • 现在会话密钥获取器 static 因此你可以在不创建购物篮实例的情况下查询篮子会话密钥信息。
    • 改变一些 TD 标签到 TH 标签以允许更好地控制basket布局。
    • 如果没有定义颜色,则在HTML输出中更改篮子的table 颜色,为白色/black。

generic  SHOP  购物  
相关文章