ASP.NET MVC SportStore 购物网示例(4)

样式(Style)

以下是我们将要创建的网页布层:

Sports Store (Header)

主页

分类1

分类2

分类 3

产品1

产品2

产品3

设计Master Page

创建 Partial View (视图控件)

右键单击 Views/Shared 打开添加 View对话框。

ASP.NET MVC SportStore 购物网示例(4)_第1张图片

ViewName为 ProductSummary选中Create a partial view。

编辑如下代码:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
 
   
<div class="item">
<h3><%=Model.Name %>h3>
 
   
<%= Model.Description %>
 
   
<h4><%=Model.Price.ToString("c") %>h4>
 
   
div>

导航和购物车

添加导航控件

1. ProductsController’s List可以根据分类筛选产品。

2. 改进路由配置使得每个分类都有一个 "clean" URL.

3. 创建一个分类列表,高亮当前选中的列表,使用Html.RenderAction方法。

根据分类筛选产品列表

添加测试达到如下目的:

当 分类 是null时的时候List()返回所有产品

根据分类名称List()返回此分类的产品

[Test]
 
   
public void List_Includes_All_Products_When_Category_Is_Null()
 
   
{
 
   
// Set up scenario with two categories
 
   
IProductsRepository repository = MockProductsRepository(
 
   
new Product { Name = "Artemis", Category = "Greek" },
 
   
new Product { Name = "Neptune", Category = "Roman" }
 
   
);
 
   
ProductsController controller = new ProductsController(repository);
 
   
controller.PageSize = 10;
 
   
// Request an unfiltered list
 
   
var result = controller.List(null, 1);
 
   
// Check that the results include both items
 
   
Assert.IsNotNull(result, "Didn't render view");
 
   
var products = (IList)result.ViewData.Model;
 
   
Assert.AreEqual(2, products.Count, "Got wrong number of items");
 
   
Assert.AreEqual("Artemis", products[0].Name);
 
   
Assert.AreEqual("Neptune", products[1].Name);
 
   
}
 
   
[Test]
 
   
public void List_Filters_By_Category_When_Requested()
 
   
{
 
   
// Set up scenario with two categories: Cats and Dogs
 
   
IProductsRepository repository = MockProductsRepository(
 
   
new Product { Name = "Snowball", Category = "Cats" },
 
   
new Product { Name = "Rex", Category = "Dogs" },
 
   
new Product { Name = "Catface", Category = "Cats" },
 
   
new Product { Name = "Woofer", Category = "Dogs" },
 
   
new Product { Name = "Chomper", Category = "Dogs" }
 
   
);
 
   
ProductsController controller = new ProductsController(repository);
 
   
controller.PageSize = 10;
 
   
// Request only the dogs
 
   
var result = controller.List("Dogs", 1);
 
   
// Check the results
 
   
Assert.IsNotNull(result, "Didn't render view");
 
   
var products = (IList)result.ViewData.Model;
 
   
Assert.AreEqual(3, products.Count, "Got wrong number of items");
 
   
Assert.AreEqual("Rex", products[0].Name);
 
   
Assert.AreEqual("Woofer", products[1].Name);
 
   
Assert.AreEqual("Chomper", products[2].Name);
 
   
Assert.AreEqual("Dogs", result.ViewData["CurrentCategory"]);
 
   
}

修改 ProoductsController类的List方法

public ViewResult List(string category, int page)
 
   
{
 
   
var productsInCategory = (category == null)
 
   
? productsRepository.Products
 
   
: productsRepository.Products.Where(x => x.Category == category);
 
   
int numProducts = productsInCategory.Count();
 
   
ViewData["TotalPages"] = (int)Math.Ceiling((double)numProducts / PageSize);
 
   
ViewData["CurrentPage"] = page;
 
   
ViewData["CurrentCategory"] = category;
 
   
return View(productsInCategory
 
   
.Skip((page - 1) * PageSize)
 
   
.Take(PageSize)
 
   
.ToList()
 
   
);
 
   
}

使用url测试 http://myweb/?category=ball

在list.aspx 添加如下代码,显示当前商品的分类:

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 
   
SportsSotre:
 
   
<%= string.IsNullOrEmpty((string)ViewData["CurrentCategory"])
 
   
? "All products"
 
   
: Html.Encode(ViewData["CurrentCategory"]) %>
 
   
asp:Content>

分类 自定义URL访问规则

自定义的url访问规则看起来会更好一些,例如:

Example URL Leads To

/ First page of “All products”

/Page2 Second page of “All products”

/Football First page of “Football” category

/Football/Page43 Forty-third page of “Football” category

/Anything/Else Else action on AnythingController

添加测试

[TestFixture]
 
   
public class InboundRoutingTests
 
   
{
 
   
public void TestRoute(string url, object expectedValues)
 
   
{
 
   
RouteCollection routes = new RouteCollection();
 
   
MvcApplication.RegisterRoutes(routes);
 
   
var mockHttpContext = new Moq.Mock();
 
   
var mockRequest = new Moq.Mock();
 
   
mockHttpContext.Setup(x => x.Request).Returns(mockRequest.Object);
 
   
mockRequest.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(url);
 
   
//act:get the mapped routed
 
   
RouteData routeData = routes.GetRouteData(mockHttpContext.Object);
 
   
//assert:Test the route values against expectations
 
   
Assert.IsNotNull(routeData);
 
   
var expectedDict = new RouteValueDictionary(expectedValues);
 
   
foreach (var expectedVal in expectedDict)
 
   
{
 
   
if (expectedVal.Value == null)
 
   
Assert.IsNull(routeData.Values[expectedVal.Key]);
 
   
else
 
   
Assert.AreEqual(expectedVal.Value.ToString(),
 
   
routeData.Values[expectedVal.Key].ToString());
 
   
}
 
   
}
 
   
[Test]
 
   
public void Slash_Goes_To_All_Products_Page_1()
 
   
{
 
   
TestRoute("~/", new { controller = "Products", action = "List", category = (string)null, page = 1 });
 
   
}
 
   
[Test]
 
   
public void Page2_Goes_To_All_Products_Page_2()
 
   
{
 
   
TestRoute("~/Page2", new
 
   
{
 
   
controller = "Products",
 
   
action = "List",
 
   
category = (string)null,
 
   
page = 2
 
   
});
 
   
}
 
   
[Test]
 
   
public void Ball_Goes_To_ball_Page_1()
 
   
{
 
   
TestRoute("~/ball", new
 
   
{
 
   
controller = "Products",
 
   
action = "List",
 
   
category = "ball",
 
   
page = 1
 
   
});
 
   
}
 
   
[Test]
 
   
public void Ball_Slash_Page43_Goes_To_ball_Page_3()
 
   
{
 
   
TestRoute("~/ball/Page3", new
 
   
{
 
   
controller = "Products",
 
   
action = "List",
 
   
category = "ball",
 
   
page = 3
 
   
});
 
   
}
 
   
[Test]
 
   
public void Anything_Slash_Else_Goes_To_Else_On_AnythingController()
 
   
{
 
   
TestRoute("~/Anything/Else", new { controller = "Anything", action = "Else" });
 
   
}
 
   
}

修改 Global.asax.cs

routes.MapRoute(null,
 
   
"", // Only matches the empty URL (i.e. ~/)
 
   
new
 
   
{
 
   
controller = "Products",
 
   
action = "List",
 
   
category = (string)null,
 
   
page = 1
 
   
}
 
   
);
 
   
routes.MapRoute(null,
 
   
"Page{page}", // Matches ~/Page2, ~/Page123, but not ~/PageXYZ
 
   
new { controller = "Products", action = "List", category = (string)null },
 
   
new { page = @"\d+" } // Constraints: page must be numerical
 
   
);
 
   
routes.MapRoute(null,
 
   
"{category}", // Matches ~/Football or ~/AnythingWithNoSlash
 
   
new { controller = "Products", action = "List", page = 1 }
 
   
);
 
   
routes.MapRoute(null,
 
   
"{category}/Page{page}", // Matches ~/Football/Page567
 
   
new { controller = "Products", action = "List" }, // Defaults
 
   
new { page = @"\d+" } // Constraints: page must be numerical
 
   
);
 
   
routes.MapRoute(null, "{controller}/{action}");

最后修改List.aspx的翻页:

<%=Html.PageLinks((int)ViewData["CurrentPage"],(int)ViewData["TotalPages"],
 
   
i=>Url.Action("List",new {page=i,
 
   
category=ViewData["currentCategory"]
 
   
})) %>

F5测试运行。

ASP.NET MVC SportStore 购物网示例(4)_第2张图片

创建分类的导航菜单

创建一个Controller

public class NavController : Controller
 
   
{
 
   
public string Menu()
 
   
{
 
   
return "Hello from NavController";
 
   
}
 
   
}

更改Site.Master

<% Html.RenderAction("Menu", "Nav"); %>

F5 运行。

显示实际的菜单,添加测试程序。

using System;
 
   
using System.Collections.Generic;
 
   
using System.Linq;
 
   
using System.Text;
 
   
using NUnit.Framework;
 
   
using WebUI.Controllers;
 
   
using DomainModel.Abstract;
 
   
using DomainModel.Entities;
 
   
using System.Web.Mvc;
 
   
namespace Tests
 
   
{
 
   
[TestFixture]
 
   
public class NavControllerTests
 
   
{
 
   
[Test]
 
   
public void Takes_IProductsRepository_As_Constructor_Param()
 
   
{
 
   
// This test "passes" if it compiles, so no Asserts are needed
 
   
new NavController((IProductsRepository)null);
 
   
}
 
   
[Test]
 
   
public void Produces_Home_Plus_NavLink_Object_For_Each_Distinct_Category()
 
   
{
 
   
// Arrange: Product repository with a few categories
 
   
IQueryable products = new[] {
 
   
new Product { Name = "A", Category = "Animal" },
 
   
new Product { Name = "B", Category = "Vegetable" },
 
   
new Product { Name = "C", Category = "Mineral" },
 
   
new Product { Name = "D", Category = "Vegetable" },
 
   
new Product { Name = "E", Category = "Animal" }
 
   
}.AsQueryable();
 
   
var mockProductsRepos = new Moq.Mock();
 
   
mockProductsRepos.Setup(x => x.Products).Returns(products);
 
   
var controller = new NavController(mockProductsRepos.Object);
 
   
// Act: Call the Menu() action
 
   
ViewResult result = controller.Menu();
 
   
// Assert: Check it rendered one NavLink per category
 
   
// (in alphabetical order)
 
   
var links = ((IEnumerable)result.ViewData.Model).ToList();
 
   
Assert.IsEmpty(result.ViewName); // Should render default view
 
   
Assert.AreEqual(4, links.Count);
 
   
Assert.AreEqual("Home", links[0].Text);
 
   
Assert.AreEqual("Animal", links[1].Text);
 
   
Assert.AreEqual("Mineral", links[2].Text);
 
   
Assert.AreEqual("Vegetable", links[3].Text);
 
   
foreach (var link in links)
 
   
{
 
   
Assert.AreEqual("Products", link.RouteValues["controller"]);
 
   
Assert.AreEqual("List", link.RouteValues["action"]);
 
   
Assert.AreEqual(1, link.RouteValues["page"]);
 
   
if (links.IndexOf(link) == 0) // is this the "Home" link?
 
   
Assert.IsNull(link.RouteValues["category"]);
 
   
else
 
   
Assert.AreEqual(link.Text, link.RouteValues["category"]);
 
   
}
 
   
}
 
   
}
 
   
}

修改Navcontroller类:

using System;
 
   
using System.Collections.Generic;
 
   
using System.Linq;
 
   
using System.Web;
 
   
using System.Web.Mvc;
 
   
using System.Web.Mvc.Ajax;
 
   
using System.Web.Routing;
 
   
using DomainModel.Abstract;
 
   
namespace WebUI.Controllers
 
   
{
 
   
public class NavController : Controller
 
   
{
 
   
private IProductsRepository productsRepository;
 
   
public NavController(IProductsRepository productsRepository)
 
   
{
 
   
this.productsRepository = productsRepository;
 
   
}
 
   
public ViewResult Menu()
 
   
{
 
   
// Put a Home link at the top
 
   
List navLinks = new List();
 
   
navLinks.Add(new CategoryLink(null));
 
   
// Add a link for each distinct category
 
   
var categories = productsRepository.Products.Select(x => x.Category);
 
   
foreach (string category in categories.Distinct().OrderBy(x => x))
 
   
navLinks.Add(new CategoryLink(category));
 
   
return View(navLinks);
 
   
}
 
   
}
 
   
public class NavLink // Represents a link to any arbitrary route entry
 
   
{
 
   
public string Text { get; set; }
 
   
public RouteValueDictionary RouteValues { get; set; }
 
   
}
 
   
public class CategoryLink : NavLink // Specifically a link to a product category
 
   
{
 
   
public CategoryLink(string category)
 
   
{
 
   
Text = category ?? "Home";
 
   
RouteValues = new RouteValueDictionary(new
 
   
{
 
   
controller = "Products",
 
   
action = "List",
 
   
category = category,
 
   
page = 1
 
   
});
 
   
}
 
   
}
 
   
}

右键单击 Menu()方法,为Menu添加一个PartialView。

ASP.NET MVC SportStore 购物网示例(4)_第3张图片

修改代码如下:

<% foreach (var link in Model)
 
   
{ %>
 
   
<a href="<%= Url.RouteUrl(link.RouteValues) %>">
 
   
<%= link.Text %>
 
   
a>
 
   
<% } %>

添加样式到css:

DIV#categories A
 
   
{
 
   
font: bold 1.1em "Arial Narrow","Franklin Gothic Medium",Arial; display: block;
 
   
text-decoration: none; padding: .6em; color: Black;
 
   
border-bottom: 1px solid silver;
 
   
}
 
   
DIV#categories A.selected { background-color: #666; color: White; }
 
   
DIV#categories A:hover { background-color: #CCC; }
 
   
DIV#categories A.selected:hover { background-color: #666; }

高亮显示分类

修改代码:

public ViewResult Menu(string highlightCategory)
 
   
{
 
   
// Put a Home link at the top
 
   
List navLinks = new List();
 
   
navLinks.Add(new CategoryLink(null)
 
   
{
 
   
IsSelected = (highlightCategory == null)
 
   
});
 
   
// Add a link for each distinct category
 
   
var categories = productsRepository.Products.Select(x => x.Category);
 
   
foreach (string category in categories.Distinct().OrderBy(x => x))
 
   
navLinks.Add(new CategoryLink(category)
 
   
{
 
   
IsSelected = (category == highlightCategory)
 
   
});
 
   
return View(navLinks);
 
   
}
 
   
}
 
   
public class NavLink // Represents a link to any arbitrary route entry
 
   
{
 
   
public string Text { get; set; }
 
   
public RouteValueDictionary RouteValues { get; set; }
 
   
public bool IsSelected { get; set; }
 
   
}
 
   
注意:更改测试 ViewResult result = contrller.Menu(null);
 
   
修改Site.master
 
   
categories">
 
   
<% Html.RenderAction("Menu", "Nav",
 
   
new { highlightCategory = ViewData["CurrentCategory"] }); %>
 
   
 
   
修改Menu.ascx
 
   
<% foreach (var link in Model)
 
   
{ %>
 
   
<%= Url.RouteUrl(link.RouteValues) %>" class="<%= link.IsSelected ? "selected" : "" %>">
 
   
<%= link.Text %>
 
   

 
   
<% } %>
 
   
创建购物车
定义购物车实体类
namespace DomainModel.Entities
 
   
{
 
   
public class Cart
 
   
{
 
   
private List lines = new List();
 
   
public IList Lines { get { return lines; } }
 
   
public void AddItem(Product product, int quantity) { }
 
   
public decimal ComputeTotalValue() { throw new NotImplementedException(); }
 
   
public void Clear() { throw new NotImplementedException(); }
 
   
}
 
   
public class CartLine
 
   
{
 
   
public Product Product { get; set; }
 
   
public int Quantity { get; set; }
 
   
}
 
   
}

测试Cart

[TestFixture]
 
   
public class CartTests
 
   
{
 
   
[Test]
 
   
public void Cart_Starts_Empty()
 
   
{
 
   
Cart cart = new Cart();
 
   
Assert.AreEqual(0, cart.Lines.Count);
 
   
Assert.AreEqual(0, cart.ComputeTotalValue());
 
   
}
 
   
[Test]
 
   
public void Can_Add_Items_To_Cart()
 
   
{
 
   
Product p1 = new Product { ProductID = 1 };
 
   
Product p2 = new Product { ProductID = 2 };
 
   
// Add three products (two of which are same)
 
   
Cart cart = new Cart();
 
   
cart.AddItem(p1, 1);
 
   
cart.AddItem(p1, 2);
 
   
cart.AddItem(p2, 10);
 
   
// Check the result is two lines
 
   
Assert.AreEqual(2, cart.Lines.Count, "Wrong number of lines in cart");
 
   
// Check quantities were added properly
 
   
var p1Line = cart.Lines.Where(l => l.Product.ProductID == 1).First();
 
   
var p2Line = cart.Lines.Where(l => l.Product.ProductID == 2).First();
 
   
Assert.AreEqual(3, p1Line.Quantity);
 
   
Assert.AreEqual(10, p2Line.Quantity);
 
   
}
 
   
[Test]
 
   
public void Can_Be_Cleared()
 
   
{
 
   
Cart cart = new Cart();
 
   
cart.AddItem(new Product(), 1);
 
   
Assert.AreEqual(1, cart.Lines.Count);
 
   
cart.Clear();
 
   
Assert.AreEqual(0, cart.Lines.Count);
 
   
}
 
   
[Test]
 
   
public void Calculates_Total_Value_Correctly()
 
   
{
 
   
Cart cart = new Cart();
 
   
cart.AddItem(new Product { ProductID = 1, Price = 5 }, 10);
 
   
cart.AddItem(new Product { ProductID = 2, Price = 2.1M }, 3);
 
   
cart.AddItem(new Product { ProductID = 3, Price = 1000 }, 1);
 
   
Assert.AreEqual(1056.3, cart.ComputeTotalValue());
 
   
}
 
   
}
 
   
运行Test失败,修改Cart类
 
   
public class Cart
 
   
{
 
   
private List lines = new List();
 
   
public IList Lines
 
   
{
 
   
get
 
   
{
 
   
return lines.AsReadOnly();
 
   
}
 
   
}
 
   
public void AddItem(Product product, int quantity)
 
   
{
 
   
var line = lines.FirstOrDefault(l => l.Product.ProductID == product.ProductID);
 
   
if (line == null)
 
   
lines.Add(new CartLine { Product = product, Quantity = quantity });
 
   
else
 
   
line.Quantity += quantity;
 
   
}
 
   
public decimal ComputeTotalValue()
 
   
{
 
   
return lines.Sum(l => l.Product.Price * l.Quantity);
 
   
}
 
   
public void Clear()
 
   
{
 
   
lines.Clear();
 
   
}
 
   
public void RemoveLine(Product product)
 
   
{
 
   
lines.RemoveAll(l => l.Product.ProductID == product.ProductID);
 
   
}
 
   
}
 
   
public class CartLine
 
   
{
 
   
public Product Product { get; set; }
 
   
public int Quantity { get; set; }
 
   
}

测试成功。

添加 "Add to Cart " 按钮

打开/Views/Shared/ProductSummary.ascx 添加 Add to Cart按钮

<div class="item">
 
   
<h3><%=Model.Name %>h3>
 
   
<%= Model.Description %>
 
   
<% using (Html.BeginForm("AddToCart", "Cart")){ %>
 
   
<%= Html.Hidden("ProductID");%>
 
   
<%=Html.Hidden("returnUrl", ViewContext.HttpContext.Request.Url.PathAndQuery)%>
 
   
<input type = "submit" value="+Add to cart" />
 
   
<% } %>
 
   
<h4><%=Model.Price.ToString("c") %>h4>
 
   
div>

添加样式

FORM { margin: 0; padding: 0; }
 
   
DIV.item FORM { float:right; }
 
   
DIV.item INPUT {
 
   
color:White; background-color: #333; border: 1px solid black; cursor:pointer;
 
   
}

F5运行测试:

ASP.NET MVC SportStore 购物网示例(4)_第4张图片

转载请注明出处! Author: [email protected]

转载于:https://www.cnblogs.com/xingquan/archive/2011/03/24/1994249.html

你可能感兴趣的:(ASP.NET MVC SportStore 购物网示例(4))