跟我学ASP.NET MVC之六:SportsStrore添加产品目录导航
摘要:
上一篇文章,我建立了SportsStore应用程序的核心架构。现在我将使用这个架构向这个应用程序添加功能,你将开始看到这个基础架构的作用。我将添加重要的面向客户的简单功能,在这个过程中,你将看到MVC框架提供的额外功能。
如果客户能够根据目录导航产品,SportsStore应用程序可用性将更高。
- 改进ProductController控制器中的List方法,让他能够过滤在repository里的产品对象。
- 修改改进URL格式,修改routing策略。
- 在网站首页旁边创建一个目录列表,高亮显示当前页。
过滤产品列表
修改视图模型ProductsListViewModel,添加当前目录信息。
using SportsStore.Domain.Entities;
using System.Collections.Generic; namespace SportsStore.WebUI.Models
{
public class ProductsListViewModel
{
public IEnumerable<Product> Products { get; set; }
public PagingInfo PagingInfo { get; set; }
public string CurrentCategory { get; set; }
}
}
修改ProductController控制器的List方法,添加参数category,根据传入的category过滤产品。
using SportsStore.Domain.Abstract;
using SportsStore.WebUI.Models;
using System.Web.Mvc;
using System.Linq; namespace SportsStore.WebUI.Controllers
{
public class ProductController : Controller
{
private IProductRepository repository; public int PageSize = ; public ProductController(IProductRepository productRepository)
{
this.repository = productRepository;
} public ViewResult List(string category, int page = )
{
ProductsListViewModel model = new ProductsListViewModel
{
Products = repository.Products.Where(p => string.IsNullOrEmpty(category) || p.Category == category)
.OrderBy(p => p.ProductID).Skip((page - ) * PageSize).Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = category == null ? repository.Products.Count() : repository.Products.Where(e => e.Category == category).Count()
},
CurrentCategory = category
};
return View(model);
}
}
}
- 如果传入的category是空字符串,则返回所有产品列表。否则根据linq的where条件获取category下的产品列表。
- 如果传入的category是空字符串,则返回所有产品数量。否则根据linq的where条件获取category下的产品数量。
- 将category赋值给视图模型对象的CurrentCategory属性。
执行程序,如果将浏览器地址栏修改成下面的形式:
http://localhost:17596/?category=Soccer
将返回category是Soccer的产品列表。
很明显,用户将不会自己使用URL导航产品目录,但是你可以看到,一旦基础架构做好了,一个小的改动在一个MVC框架的应用程序起到了大的作用。
修改定义URL格式
没有人想看到或者使用丑陋的像/?category=Soccer一样的URL。为了改进这个,我将修改路由格式,创建更适合我和客户的生成URL的方法。
修改RouteConfig.cs文件的RegisterRoutes方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing; namespace SportsStore
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(
name: null,
url: "",
defaults: new { controller = "Product", action = "List", category = (string)null, page = }
); routes.MapRoute(
name: null,
url: "Page{page}",
defaults: new { controller = "Product", action = "List", category = (string)null },
constraints: new { page = @"\d+" }
); routes.MapRoute(
name: null,
url: "{category}",
defaults: new { controller = "Product", action = "List", page = }
); routes.MapRoute(
name: null,
url: "{category}/Page{page}",
defaults: new { controller = "Product", action = "List" },
constraints: new { page = @"\d+" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }
);
}
}
}
下面来解释添加的路由信息。
第一个路由函数:
url: "":表示这个是默认路由。defaults设置参数默认值,将category的空字符串设置为默认值,page的默认值是1。这样,如果访问URL:http://localhost:17596/,category为空字符串,page值为1。
第二个路由函数:
url: "Page{page}":表示路由的URL格式是/Page{page}。defaults设置参数category的默认值为空字符串。这样,如果访问URL:http://localhost:17596/Page2,category为空字符串,page值为传入的值2。
第三个路由函数:
url: "{category}":表示路由的URL格式是/{category}。defaults设置参数page的默认值为1。这样,如果访问URL:http://localhost:17596/Soccer,category为字符串Soccer,page值为默认值1。
第四个路由函数:
url: "{category}/Page{page}":表示路由的URL格式是/{category}/Page{page}。defaults不用设置参数默认值。这样,如果访问URL:http://localhost:17596/Soccer/Page2,category为字符串Soccer,page值为传入的值2。
MVC使用ASP.NET路由系统来处理客户端到来的请求,但是它还用来向外生成符合URL定义格式的URL字符串,嵌入到Web页面上。于是,我可以保证应用程序的所有的URL都是一致的。
定义好路由表后,需要修改List视图上生成链接字符串的方法调用。
@model SportsStore.WebUI.Models.ProductsListViewModel @{
ViewBag.Title = "Products";
} @foreach (var p in Model.Products)
{
Html.RenderPartial("ProductSummary", p);
}
<div class="btn-group pull-right">
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x, category = Model.CurrentCategory }))
</div>
Url.Action方法是向外产生链接最方便的方法。这里添加将category传入生成链接的代理方法中。
运行程序,改变浏览器地址栏上的URL,得到各路由URL页面结果。
创建目录导航菜单
首先创建MenuController控制器。
using SportsStore.Domain.Abstract;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc; namespace SportsStore.WebUI.Controllers
{
public class NavController : Controller
{
private IProductRepository repository; public NavController(IProductRepository productRepository)
{
repository = productRepository;
} public PartialViewResult Menu(string category = null)
{
ViewBag.SelectedCategory = category;
IEnumerable<string> categories = repository.Products.Select(x => x.Category).Distinct().OrderBy(x => x);
return PartialView("Menu", categories);
}
}
}
- 这个控制器返回PartialViewResult类型对象。PartialViewResult类跟之前使用的ViewResult类型都是继承自基类ViewResultBase。
- ViewBag动态类型属性SelectedCategory设置为当前目录字符串。
- 参数category将获取路由产生的URL上的目录字符串。
- categories变量返回Product下所有目录。
- 调用方法PartialView方法,返回PartialViewResult类型对象,传入的参数是视图名称和视图模型的变量。
然后创建Menu视图。
@model IEnumerable<string> <div>
@Html.RouteLink("Home", new { controller = "Product", action = "List" }, new { @class = "btn btn-block btn-default btn-lg" })
@foreach (var link in Model)
{
@Html.RouteLink(link, new
{
controller = "Product",
action = "List",
category = link,
page = 1
}, new
{
@class = ("btn btn-block btn-default btn-lg") + (link == ViewBag.SelectedCategory ? " btn-primary" : "")
})
}
</div>
这里调用Html扩展方法RouteLink,生成链接字符串。当然,你也可以使用ActionLink方法。
- 方法的第一个参数是链接显示的文本字符串。
- 第二个参数是动态数据类型,用来指定控制器,控制器方法以及方法传入的参数。
- 第三个参数也是动态数据类型,使用@class来指定链接的样式。这里使用bootstrap的样式,这里的设置顺序是:btn指定按钮样式,btn-block指定按钮呈现的外观占整行,btn-default指定按钮的皮肤颜色等,btn-lg指定按钮的大小。
- 如果是当前页,将添加btn-primary样式,将该按钮高亮显示。
最后,修改_Layout.cshtml文件,将Menu显示在页面上。
<!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
<title>@ViewBag.Title</title>
</head>
<body>
<div class="navbar navbar-inverse" role="navigation">
<a class="navbar-brand" href="#">SPORTS STORE</a>
</div>
<div class="row panel">
<div class="col-xs-3">
@Html.Action("Menu", "Nav")
</div>
<div class="col-xs-8">
@RenderBody()
</div>
</div>
</body>
</html>
这里调用Html的扩展方法Action,传入控制器名字和方法名字,生成HTML字符串。它将Menu视图生成的字符串嵌入到div里面,生成导航栏。
注意这里不是调用RenderPartial方法,RenderPartial方法返回的void,它跟C#语句嵌套使用,在视图上嵌入视图。
运行程序,得到运行结果。
如果点击Soccer目录,将返回Category是Soccer的产品列表。并将Soccer这个按钮高亮显示。
跟我学ASP.NET MVC之六:SportsStrore添加产品目录导航的更多相关文章
- 跟我学ASP.NET MVC之五:SportsStrore开始
摘要: 这篇文章将介绍一个ASP.NET应用程序SportsStore的开发过程. 开始 创建解决方案 创建工程 在New ASP.NET Project - SportsStore窗口中,选择Emp ...
- 跟我学ASP.NET MVC之七:SportsStrore一个完整的购物车
摘要: SportsStore应用程序进展很顺利,但是我不能销售产品直到设计了一个购物车.在这篇文章里,我就将创建一个购物车. 在目录下的每个产品旁边添加一个添加到购物车按钮.点击这个按钮将显示客户到 ...
- 跟我学ASP.NET MVC之三:完整的ASP.NET MVC程序-PartyInvites
摘要: 在这篇文章中,我将在一个例子中实际地展示MVC. 场景 假设一个朋友决定举办一个新年晚会,她邀请我创建一个用来邀请朋友参加晚会的WEB程序.她提出了四个注意的需求: 一个首页展示这个晚会 一个 ...
- 跟我学ASP.NET MVC之二:第一个ASP.NET MVC程序
摘要: 本篇文章带你一步一步创建一个简单的ASP.NET MVC程序. 创建新ASP.NET MVC工程 点击“OK”按钮后,打开下面的窗口: 这里选择“Empty”模板以及“MVC”选项.这次不创 ...
- 跟我学ASP.NET MVC之一:开篇有益
摘要: ASP.NET MVC是微软的Web开发框架,结合了模型-视图-控制器(MVC)架构的有效性和整洁性,敏捷开发最前沿的思想和技术,以及现存的ASP.NET平台最好的部分.它是传统ASP.NET ...
- [转]我要学ASP.NET MVC 3.0(十二): MVC 3.0 使用自定义的Html控件
本文转自:http://www.cnblogs.com/lukun/archive/2011/08/05/2128693.html 概述 在ASP.NET MVC框架中已经封装了很多基于Html标 ...
- 跟我学ASP.NET MVC之十一:URL路由
摘要: 在MVC框架之前,ASP.NET假定在请求的URLs和服务器硬盘文件之间有直接的关系.服务器的职责是接收浏览器请求,从相应的文件发送输出. 这种方法只能工作于Web表单,每一个ASPX页面既是 ...
- ASP.NET MVC 5 系列 学习笔记 目录 (持续更新...)
前言: 记得当初培训的时候,学习的还是ASP.NET,现在回想一下,图片水印.统计人数.过滤器....HttpHandler是多么的经典! 不过后来接触到了MVC,便立马爱上了它.Model-View ...
- 给Asp.Net MVC及WebApi添加路由优先级
一.为什么需要路由优先级 大家都知道我们在Asp.Net MVC项目或WebApi项目中注册路由是没有优先级的,当项目比较大.或有多个区域.或多个Web项目.或采用插件式框架开发时,我们的路由注册很可 ...
随机推荐
- Treemap 有序的hashmap。用于排序
TreeMap:有固定顺序的hashmap.在需要排序的Map时候才用TreeMap. Map.在数组中我们是通过数组下标来对其内容索引的,键值对. HashMap HashMap 用哈希码快速定位一 ...
- 初探linux子系统集之led子系统(二)
巴西世界杯,德国7比1东道主,那个惨不忍睹啊,早上起来看新闻,第一眼看到7:1还以为点球也能踢成这样,后来想想,点球对多嘛6比1啊,接着就是各种新闻铺天盖地的来了.其实失败并没有什么,人生若是能够成功 ...
- 解决Cell重用内容混乱的几种简单方法,有些方法会增加内存
重用实现分析 查看UITableView头文件,会找到NSMutableArray* visiableCells,和NSMutableDictnery* reusableTableCells两个结构 ...
- hover变化图片
<div class="icon width mar"> <div class="cpzs_tit"></div> < ...
- Python list 两个不等长列表交叉合并
遇到一个需求,需要对两个长度不一定相等的列表进行交叉合并.像拉拉链一样(两边的拉链不一定相等). 如: a = [1, 3, 5] b = [2, 4, 6, 8] 需将a, b 合并为 c c = ...
- Spring对事务管理的支持的发展历程(基础篇)
1.问题 Connection conn = DataSourceUtils.getConnection(); //开启事务 conn.setAutoCommit(false); try { Obje ...
- Spring3 MVC使用@ResponseBody的乱码问题及解决办法
近日用Spring3的MVC写东西,深感其之于Webwork/Struts2的便利,但是在通过@ResponseBody这个annotation输出一个json字符串的时候,发现页面上获得的json字 ...
- Jenkins实现Android自动化打包
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/77102359 本文出自[赵彦军的博客] 1.Tomcat 进入 https://t ...
- C#文件和字节流的转换方法
1.读取文件,并转换为字节流 FileStream fs = new FileStream(filename,FileMode.Open,FileAccess.Read); byte[] infbyt ...
- oracle 触发器,当一个表更新或插入时将数据同步至另个库中的某个表中
有两个表分别是 A用户下的 T_SRC_WEATHER_TSPG字段如图, B用户下的t_src_weather 表,如图: 要求,当A用户下的T_SRC_WEATHER_TSPG表有插入或者更新数据 ...