前几天看见博客园上有人写ASP.NET MVC的分页思想,这让我不禁想起了PagedList。PagedList是NuGet上提供的一个分页的类库,能对任何IEnumerable<T>进行分页,而且非常简单好用。从NuGet上,可以获取两个DLL:PagedList.dll和PagedList.Mvc.dll。PagedList.dll提供分页的核心操作,PagedList.Mvc.dll是一个辅助类库,在创建分页的UI时候提供简单、可扩展的创建方法。不过PagedList.dll可以用于MVC2及其以上,但是PagedList .Mvc.dll只能用于MVC3(及其以上)。
使用PagedList:
(一)、安装PagedList:引用-->Add Library Package Reference--->OnLine All--->搜索PagedList,点击Install安装。(如果没有安装Nuget,可以到下面地址下载:http://www.nuget.org/)
(二)、NuGet的好处就是我们不用再进行web.config等各种复杂的配置,所以下面直接编码:
using PagedList; ..... //Controller:PersonController public ViewResult Index(int? page) { int pageNumber = page ?? 1; int pageSize = 2; var persons = db.Persons.ToList(); return View(persons.ToPagedList(pageNumber, pageSize)); } ...... //View:Views/Person/Index
@model PagedList.PagedList<XXX.Person>
...... <div> Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount @if (Model.HasPreviousPage) { @Html.ActionLink("<<", "Index", new { Page = 1 }) @Html.Raw(" ") @Html.ActionLink("< Prve", "Index", new { Page = Model.PageNumber - 1 }) } else { @:<< @Html.Raw(" ") @:< Prev } @Html.Raw(" ") @if (Model.HasNextPage) { @Html.ActionLink("Next >", "Index", new { Page = Model.PageNumber + 1 }) @Html.Raw(" ") @Html.ActionLink(">>", "Index", new { Page = Model.PageCount }) } else { @:Next> @Html.Raw(" ") @:>> } </div>
是不是很简单一句简单的ToPageList就会返回一个强类型的PagedList.PagedList<T>对象,而且PagedList.PagedList<T>实现了IPagedList接口,通过对象浏览器我们可以看到IPagedList提供了很多方法和属性供我们在View绑定时候使用(例如HasPreviousPage、HasNextPage、PageCount、PageNumer等等),如下图:
不过还有两个问题:
第一、看了Controller中的代码,第一感觉是糟了。db.Persons.ToList() 太危险了,ToList时候数据已经执行,幻想一下,如果是百万甚至千万级的数据。。。所以这样不行。因为这里没有起到分页的效果。解决思路,赶紧组织其执行。
第二、View中绑定分页还有没有更好的方法,每次这样写是不是太费劲了呢?答案是肯定的,PagedList.Mvc.dll提供了分页导航功能。
利用PagedList优化分页:
//Controller: PersonController public ViewResult Index(int? page) { int pageNumber = page ?? 1; int pageSize = 2; //Skip之前必须orderby var persons = from p in db.Persons orderby p.PersonID descending select p; return View(persons.ToPagedList(pageNumber, pageSize)); }
ok,这样就可以在分页前阻止查询的执行了。但是这里看到还是很虚的。PagedList为我们提供了StaticPagedList<T>类,而且我个人也比较推崇用它来进行分页查询:
public StaticPagedList(System.Collections.Generic.IEnumerable<T> subset, int pageNumber, int pageSize, int totalItemCount)
可以看到,StaticPagedList需要将:某一页的数据、页码、每页数据的容量、和数据总条目传入。也就是说这时候StaticPagedList不再像PagedList一样承担数据的划分工作,而仅仅承担数据的绑定操作。good。看个例子:
public ViewResult IndexTwo(int? page) { int pageIndex = page ?? 1; int pageSize = 2; int totalCount = 0; var persons = GetPerson(pageIndex, pageSize, ref totalCount); var personsAsIPagedList = new StaticPagedList<Person>(persons, pageIndex, pageSize, totalCount); return View(personsAsIPagedList); } public List<Person> GetPerson(int pageIndex, int pageSize, ref int totalCount) { var persons = (from p in db.Persons orderby p.PersonID descending select p).Skip((pageIndex - 1) * pageSize).Take(pageSize); totalCount = db.Persons.Count(); return persons.ToList(); }
不过这里注意View中的@model PagedList.PagedList<XXX.Person>得换成@model PagedList.StaticPagedList<XXX.Person>额,因为返回的强类型对象不一样了。
PagedList.Mvc设置分页导航:
和上面一样安装Package.Mvc.dll,接下来看我们如何秒杀分页导航:
@model PagedList.StaticPagedList<XXX.Person> @using PagedList @using PagedList.Mvc ...... <link href="/Content/PagedList.css" rel="stylesheet" type="text/css" /> ...... <div> @Html.PagedListPager((IPagedList)Model, page => Url.Action("IndexPagedListMvc", new { page = page })) </div>
将上面代码替换掉刚才View:Views/Person/Index中的代码,完了,你可以看到分页导航几乎和上面的差不多。PagedListed.MVC封装了分页导航的代码,如下图:
可以看到上面的HtmlHelper主要提供了两个扩展方法:PagedListPager和PagedListGoToPageForm。其中PagedListPager主要提供“上一页、下一页......”这类的导航方式(这里不知道怎么描述了),而PagedListGoToPageForm提供了input输入页面点击条状的导航方式,而PagedListRenderOptions和GoToFormRenderOptions分别问它们提供了配置选项。
PagedListPager配置选项
上面的例子使用了默认的配置选项,PagedListRenderOptions还提供了一下其他的配置选项,格式如下:
@Html.PagedListPager((IPagedList)Model, page => Url.Action("IndexPagedListMvc", new { page = page }),PagedListRenderOptions.XXX);
PagedListRenderOptions.XXX提供了更多配置属性,上面所说的配置选项也仅仅是这些配置属性的组合,我们还可以自己配置属性组合如下:
@Html.PagedListPager((IPagedList)Model, page => Url.Action("IndexPagedListMvc", new { page = page }),new PagedListRenderOptions{ LinkToPreviousPageFormat = "上一页", LinkToNextPageFormat = "下一页",MaximumPageNumbersToDisplay=5 });
越看越简单哇?哈哈哈除此之外,PagedList还为我们提供了分页导航的样式。上面的<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />就是引入分页导航的样式。你安装了PagedList.Mvc会自动的放在你的Content中,这既是NuGet的好处啊。。。。当然你也可以自定义Css和引用其他的Css,这里推荐一个最经一直比较火的Twiter BootStrap(官网地址为:http://twitter.github.com/bootstrap/,表示看不懂....什么12分格系统等看的我是一头雾水)的样式:
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
PagedListGoToPageFrom及其配置
@Html.PagedListGoToPageForm((IPagedList)Model, "IndexPagedListMvc");
@Html.PagedListGoToPageForm((IPagedList)Model, "IndexPagedListMvc",new GoToFormRenderOptions { XXX=xxx });
和PagedListRenderOptions不同,的GoToFromRenderOptions没有配置选项,其他的都差不多。
到目前为止,貌似两个问题都已经完全解决了,不过好的用户体验的分页肯定不希望点击下一页后有页面刷新的操作-------Ajax。由于涉及到jquery.templ.js,而我以前没有见过这个东东,所以先放下一下,后头再来说说PagedList+Ajax的分页和PagedList的实现原理。