前段時間整mvc的分頁,倒是很順利,參考了以下幾篇博客,啟發很大。
http://www.cnblogs.com/tangmingjun/archive/2012/05/30/2526301.html
http://www.cnblogs.com/tangmingjun/archive/2012/05/31/2528732.html
順便擴展了需求,在分頁的基礎上,繼續做關鍵字查詢。
用PagedList生成的頁碼鏈接雖然樣式很漂亮,但是要做到無刷新的分頁,PagedList自動生成的代碼是不夠用的,可以配合jsRender和Ajax做上面的效果,同時手動構建PagedList生成的頁碼標簽,根據自己的需要設置href和onclick事件。
直接上代碼
前台:

@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>商品信息</h2> <p> @Html.ActionLink("新建", "Create") | @Html.Editor("txtSearch") <input type="button" value="查找" id="btnSearch" /> <img id="imgWaiting" src="~/Images/loading.gif" style="display:none; width:30px; height:30px;" /> </p> <table class="table"> <thead> <tr> <th>商品名稱</th> <th>商品描述</th> <th>原價</th> <th>特價(現價)</th> <th>銷量</th> <th>修改日期</th> <th>商品鏈接</th> <th>狀態</th> <th>圖片</th> <th></th> </tr> </thead> <tbody id="tblContent"></tbody> </table> <div id="divPage"></div> <script type="text/x-jsrender" id="contentTemplate"> <tr> <td>{{:GoodName}}</td> <td>{{:Description}}</td> <td>{{:OriginalPrice}}</td> <td>{{:SpecialPrice}}</td> <td>{{:Sales}}</td> <td>{{:ActiveTime}}</td> <td>{{:Url}}</td> <td>{{:Status}}</td> <td>{{:PicPath}}@*<img id="PicPath" src="{{:PicPath}}" class="thumbnailPdt" />*@</td> <td> <a href="/Admin/Goods/Edit/{{:GoodID}}">Edit</a> | <a href="/Admin/Goods/Delete/{{:GoodID}}">Delete</a> </td> </tr> </script> @section Scripts{ <script src="~/Scripts/jsrender.min.js" type="text/javascript"></script> <script> var key = ""; function doSearch(page) { key = $("#txtSearch").val(); $.post( "/Admin/Goods/SearchProduct", { keyword: key, pageNo: page }, function (data, status) { //數據寫到前台 $("#tblContent").html($("#contentTemplate").render(data.contents)); $("#divPage").html(data["pageList"]); }, "json" ); }; //綁定點擊查詢事件 $("#btnSearch").click(function () { doSearch(1); }); //輸入框回車開始查詢 $("#txtFilter").bind("keypress", function () { if (event.keyCode == 13) { $("#btnSearch").click(); return false; } }); //加載初始化數據 $(function () { doSearch(1); }); </script> }
后台Controller:

public ActionResult Index() { return View(); } public JsonResult SearchProduct(string keyword, int? pageNo) { int pageIndex = pageNo ?? 1; int totalCount; var goods = GetSearchData(keyword, ref pageIndex, _PageSize, out totalCount); int totalPage = totalCount / _PageSize + (totalCount % _PageSize == 0 ? 0 : 1); var pagerHtml = ExHtml.CreatePagesHtml(totalPage, pageIndex); return Json(new { contents = goods, pageList = pagerHtml }, JsonRequestBehavior.DenyGet); } List<Good> GetSearchData(string keyword, ref int pageIndex, int pageSize, out int totalCount) { var linq = from p in db.Goods select p; if (!string.IsNullOrWhiteSpace(keyword)) { linq = linq.Where(t => t.GoodID.ToString() == keyword || t.GoodName.Contains(keyword) || t.Description.Contains(keyword) ); } totalCount = linq.Count(); //修正pageIndex var maxPage = totalCount / pageSize + (totalCount % pageSize == 0 ? 0 : 1); if (pageIndex < 1) pageIndex = 1; if (pageIndex > maxPage) pageIndex = maxPage; linq = linq.OrderByDescending(x => x.GoodID) .Skip((pageIndex - 1) * pageSize) .Take(pageSize); return linq.ToList(); }
擴展函數:

public static string CreatePagesHtml(int totalPageNo, int currPageNo, string jsClickName = "doSearch") { if (currPageNo > totalPageNo || currPageNo < 1 || totalPageNo == 1) return ""; string res = ""; if (currPageNo > 3) res += LiForFirst(jsClickName);//有跳轉到第一頁 //if (currPageNo > 2) res += LiForPrev(currPageNo - 1, jsClickName);//跳轉到前一頁 if (currPageNo > 4) res += LiForOthers();//無跳轉,顯示中間有頁面 if (currPageNo > 2) res += LiForPage(currPageNo - 2, jsClickName);//前兩頁 if (currPageNo > 1) res += LiForPage(currPageNo - 1, jsClickName);//前一頁 res += LiForCurr(currPageNo);//當前頁 if (currPageNo < totalPageNo) res += LiForPage(currPageNo + 1, jsClickName);//下一頁 if (currPageNo < totalPageNo - 1) res += LiForPage(currPageNo + 2, jsClickName);//下兩頁 if (currPageNo < totalPageNo - 3) res += LiForOthers();//無跳轉,顯示中間有頁面 //if (currPageNo < totalPageNo - 1) res += LiForNext(currPageNo + 1, jsClickName);//跳轉到后一頁 if (currPageNo < totalPageNo - 2) res += LiForLast(totalPageNo, jsClickName);//跳轉到最后頁 return string.Format(@" <div class=""pagination-container""> <ul class=""pagination""> {0} </ul> </div>" , res); } static string GetPageItem(int pageNo, string text, string jsClickName, string classStr = null, string rel = null) { return string.Format(@"<li{0}><a href=""#""{1}{2}>{3}</a></li>" , string.IsNullOrWhiteSpace(classStr) ? "" : " class=\"" + classStr + "\"" , string.IsNullOrWhiteSpace(rel) ? "" : " rel=\"" + rel + "\"" , string.IsNullOrWhiteSpace(jsClickName) ? "" : string.Format(" onclick=\"{0}({1});\"", jsClickName, pageNo) , text ); } static string LiForFirst(string jsClickName) { return GetPageItem(1, "1"/*"««"*/, jsClickName, classStr: "PagedList-skipToFirst"); } static string LiForPrev(int pageNo, string jsClickName) { return GetPageItem(pageNo, "«", jsClickName, classStr: "PagedList-skipToPrevious", rel: "prev"); } static string LiForNext(int pageNo, string jsClickName) { return GetPageItem(pageNo, "»", jsClickName, classStr: "PagedList-skipToNext", rel: "next"); } static string LiForLast(int totalPageNo, string jsClickName) { return GetPageItem(totalPageNo, totalPageNo.ToString()/*"»»"*/, jsClickName, classStr: "PagedList-skipToLast"); } static string LiForPage(int pageNo, string jsClickName) { return GetPageItem(pageNo, pageNo.ToString(), jsClickName); } static string LiForCurr(int pageNo) { return GetPageItem(pageNo, pageNo.ToString(), null, classStr: "active"); } static string LiForOthers() { return GetPageItem(0, "…", null, classStr: "disabled PagedList-ellipses"); }
前台的關鍵代碼是js函數doSearch,通過post發送請求,請求成功后處理返回的json數據,並重新加載頁面部分元素。json數據包有兩個屬性,一個contents,里面是類型為List<T>的數據,通過jsrender的調用,生成前台表格的tbody中的代碼;另一個pageList是后台組裝好的,顯示page按鈕的string。
后台Controller代碼中,主要是第二個函數SearchProduct,它可以作為一個controller下面的action調用,在里面執行查詢數據,以及組裝pageHtml的操作,並打包這些數據成json數據,返回給前台。
擴展函數代碼則主要是組裝page的string結果。這部分參考PagedList生成的頁碼標簽的代碼,樣式在bootstrap里面都有。
環境:VS2013 + MVC5.0 + Bootstrap 3.0
JsRender下載地址