SportsStore是《精通ASP.NET MVC3框架(第三版)》中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器、URL优化、导航、分页、购物车、订单、产品管理、图像上传......是不错的MVC实践项目,但该项目不是放在多层框架下开发的,离真实项目还有一段距离。本系列将尝试在多层框架下实现SportsStore项目,并用自己的方式实现一些功能。
本篇为系列第四篇,包括:
■ 7、添加分页
7、添加分页
关于分页,是一系列的a标签,可以通过扩展HtmlHelper来实现。产生分页a标签的大致思路为:
1、扩展方法需要遍历循环所有页数,而所有页数是由"总记录数/页容量",再经过计算得到。
2、可以把总记录数、页容量、所有页数等封装成一个类提供给扩展方法。
3、而包含总记录数、页容量、所有页数等这个类实例,当然是由控制器方法提供。
创建PagingInfo来包装分页相关信息:
using System; namespace MySportsStore.WebUI.Models { public class PagingInfo { public int TotalItems { get; set; } public int ItemsPerPage { get; set; } public int CurrentPage { get; set; } public int TotalPages { get { return (int)Math.Ceiling((decimal) TotalItems/ItemsPerPage); } } } }
关于CurrentPage属性,扩展方法会根据它来确定是否要给a标签添加一个表示选中、当前页的css属性。
扩展方法遍历循环所有页数,产生数字a标签,并根据PagingInfo的CurrentPage属性来判断是否要给a标签加上一个用来表示选中、当前页的属性:
using System; using System.Text; using System.Web.Mvc; using MySportsStore.WebUI.Models; namespace MySportsStore.WebUI.HtmlHelpers { public static class PagingHelpers { public static MvcHtmlString PageLinks(this HtmlHelper html, PagingInfo pagingInfo, Func<int, string> pageUrl) { StringBuilder result = new StringBuilder(); for (int i = 1; i <= pagingInfo.TotalPages; i++) { TagBuilder tag = new TagBuilder("a"); tag.MergeAttribute("href", pageUrl(i)); tag.InnerHtml = i.ToString(); if (i == pagingInfo.CurrentPage) { tag.AddCssClass("selected"); } result.Append(tag.ToString()); } return MvcHtmlString.Create(result.ToString()); } } }
以上的pageUrl是一个委托,输入页面,输出一个包含查询字符串的URL。
而添加的扩展方法需要在Views文件夹下的Web.config中注册后,才能在视图页中使用:
<system.web.webPages.razor> <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <pages pageBaseType="System.Web.Mvc.WebViewPage"> <namespaces> ...... <add namespace="MySportsStore.WebUI.HtmlHelpers" /> </namespaces> </pages> </system.web.webPages.razor>
当然,分页是有关Product的分页,在显示Product列表的视图页上,除了分页,还有一个Product的集合,于是,我们可以把分页和Product的集合整合成一个视图模型:
using System.Collections.Generic; using MySportsStore.Model; namespace MySportsStore.WebUI.Models { public class ProductsListViewModel { public IEnumerable<Product> Products { get; set; } public PagingInfo PagingInfo { get; set; } } }
Product控制器的List方法职责很明确:就是根据前台视图传递过来表示当前页的page变量,产生一个ProductsListViewModel的实例,传递给前台视图:
using System.Web.Mvc; using MySportsStore.IBLL; using MySportsStore.WebUI.Models; using Ninject; namespace MySportsStore.WebUI.Controllers { public class ProductController : BaseController { [Inject] public IProductService ProductService { get; set; } public ProductController() { this.AddDisposableObject(ProductService); } public int PageSize = 4; public ViewResult List(int page = 1) { int totalCount = 0; ProductsListViewModel viewModel = new ProductsListViewModel() { Products = ProductService.LoadPageEntities(p => true, p => p.Id, PageSize, page, out totalCount, true), PagingInfo = new PagingInfo(){CurrentPage = page, ItemsPerPage = PageSize, TotalItems = ProductService.Count(p => true)} }; return View(viewModel); } } }
Product/List.cshtml强类型视图:
@model MySportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "List"; Layout = "~/Views/Shared/_Layout.cshtml"; } @foreach (var item in Model.Products) { <div class="item"> <h3>@item.Name</h3> @item.Description <h4>@item.Price.ToString("c")</h4> </div> } <div class="pager"> @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x})) </div>
运行,得到如下结果:
让人欣慰的是:对Prouct列表进行了分页处理。稍有不足的是:页面链接以http://localhost/?page2这种形式存在。我们在路由设置中设置如下路由:
routes.MapRoute( null, "Page{page}", new {controller = "Product", action = "List"} ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional } );
运行,得到如下结果:
在Views/Shared/_Layout.chstml中布局页面,设置CSS,呈现如下页面:
在Product/List.cshtml视图页中,还可以把显示产品的逻辑放到一个部分视图中,以便重复利用。在Views/Shared下创建ProductSummary.cshtml强类型部分视图:
@model MySportsStore.Model.Product <div class="item"> <h3>@Model.Name</h3> @Model.Description <h4>@Model.Price.ToString("c")</h4> </div>
最后,在Product/List.cshtml视图页中,在遍历Product集合的时候,加载部分视图:
@model MySportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "List"; Layout = "~/Views/Shared/_Layout.cshtml"; } @foreach (var item in Model.Products) { Html.RenderPartial("PrductSummary", item); } <div class="pager"> @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x})) </div>
至此,完成分页。
“MVC项目实践,在三层架构下实现SportsStore”系列包括: