前幾天看見博客園上有人寫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的實現原理。
