詳解ASP.NET MVC分頁的實現(xiàn)方法
ASP.NET MVC分頁已經(jīng)有很多文章講述過,這里我們將講述的ASP.NET MVC分頁的實現(xiàn),需要靠自己設(shè)計,這就需要了解分頁的原理,以及相關(guān)數(shù)據(jù)庫的操作。51CTO編輯推薦《ASP.NET MVC框架視頻教程》
在ASP.NET中,GridView控件本身就帶有分頁的功能,只要把當前頁面的索引值賦給GridView的PageIndex就可以實現(xiàn)了分頁,至于數(shù)據(jù)怎么分頁,全都由GridView封裝起來了。
ASP.NET MVC分頁的實現(xiàn)就只能靠自己來設(shè)計了。首先來說一下分頁的原理?,F(xiàn)在有一個張news表,中間有很多信息,我們假設(shè)一頁顯示5條記錄,這樣,頁面數(shù)量=總記錄數(shù)/5;這里,如果出現(xiàn)小數(shù),得進位取整。如比總記錄和數(shù)為51,那么分的頁面數(shù)量是11,最后一頁只有一條記錄。好,有這樣一個思路后,現(xiàn)在就來做ASP.NET MVC中的分頁了。
1、 數(shù)據(jù)庫和表
創(chuàng)建一個Data_Company的數(shù)據(jù)庫,建一張news表,表結(jié)構(gòu)如下圖:
ID是主鍵,并助是自動增長列。
2、 現(xiàn)在,我們用O/R Designer來創(chuàng)建news表的LINQ To SQL的實體類。
創(chuàng)建一個名為MvcCompany的ASP.NET MVC Web Application項目,然后選中Models,右鍵,“添加”,“新建項”,選中C#中的“數(shù)據(jù)”,如下圖:
選擇“LINQ to SQL類”,名稱設(shè)為“CompanyData.dbml”,然后“添加”。
打開“服務器資源管理器”,創(chuàng)建連接,展開表,找到news表,拖到CompanyData.dbml的左邊表視圖區(qū)(注:.dbml視圖左邊是SQL表和SQL視圖區(qū),右邊是SQL存儲過程和SQL函數(shù)區(qū)),效果如下圖:
在“解決方案資源管理器”下的,“Models”多了三個文件,CompanyData.dbml,展開它,會有CompanyData.dbml.layout和CompanyData.designer.cs,前者是CompanyData.dbml視圖的一些信息(比如news表在的坐標等信息),后者是news實體類及Data_Company數(shù)據(jù)庫的類,在數(shù)據(jù)庫類中聚合了news實體類。
3、 添加news列表類。因為一個news實體類,一次只能表示一條記錄,如果呈現(xiàn)一個news表中的數(shù)據(jù),最好定義一個集合類來存臨時來存放news記錄的集合。
類的實現(xiàn)如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- namespace MvcCompany.Models
- {
- public class NewList<T> : List<T>
- {
- /**//// <summary>
- /// 頁面索引值
- /// </summary>
- public int PageIndex { get; private set; }
- /**//// <summary>
- /// 每頁記錄的數(shù)量
- /// </summary>
- public int PageSize { get; private set; }
- /**//// <summary>
- /// 記錄總條數(shù)
- /// </summary>
- public int TotalCount { get; private set; }
- /**//// <summary>
- /// 共有的頁數(shù)和
- /// </summary>
- public int TotalPages { get; private set; }
- public NewList(IQueryable<T> source, int pageIndex, int pageSize)
- {
- PageIndex = pageIndex;
- PageSize = pageSize;
- TotalCount = source.Count();
- // 進上去取整( 總記錄條數(shù)/一面記錄的條數(shù))
- TotalPages = (int)Math.Ceiling(TotalCount / (double)pageSize);
- this.AddRange(source.Skip(pageIndex * pageSize).Take(PageSize));
- }
- /**//// <summary>
- /// 是否存在前續(xù)頁
- /// </summary>
- public bool HasPreviousPage
- {
- get { return (PageIndex > 0); }
- }
- /**//// <summary>
- /// 是否存在后續(xù)頁
- /// </summary>
- public bool HasNextPage
- {
- get { return (PageIndex + 1 < AllPages); }
- }
- }}
這里,我們實現(xiàn)了一個泛型的集合列表NewList(當然,這里的本質(zhì)上可以當其他實體類的集合列表),在這個類中間,有四個字段,訪問修飾符都是public的,分另為:
PageIndex:當前頁面的索引值
PageSize:每個頁面的記錄的條數(shù)
AllCount:記錄的總條數(shù)
AllPages:共有的頁面總數(shù)
在NewList 構(gòu)造函數(shù)中,有如下代碼:
- public NewList(IQueryable<T> list, int pageIndex, int pageSize)
- {
- PageIndex = pageIndex;
- PageSize = pageSize;
- AllCount = source.Count();
- AllPages = (int)Math.Ceiling(AllCount / (double)pageSize);
構(gòu)造函的參數(shù)有三個,一個是list,就是實體類的一個集合,還有就是頁面索引值和每個頁面的記錄條數(shù)。
第1、2代碼很容易理角,第3行代碼是得到列表的總記錄條數(shù),第4行代碼,就完成了我們在開始時分析的分頁實現(xiàn)的公式:頁面數(shù)量=總記錄數(shù)/每頁記錄數(shù),其中Math.Ceiling就是把小數(shù)部分進到整數(shù)的函數(shù)。
最關(guān)鍵的是第5行代碼,首先看Skip(PageIndex*PageSize),頁面索引值乘上每頁記錄數(shù),得到是當前頁面以前的所有記錄數(shù),Skip是跳過這些記錄,而得到后面的所有記錄,Take(PageSize)是得到PageSize條數(shù)的記錄,比如,我們想要第三頁的記錄,這個頁面的索引值為2(因為索引值從0開始)PageIndex=2,每頁顯示5條記錄,PageSize=5,就是要跳過list中的前10條記錄,然后再取前5條記錄,即取list中的第11條到第15條記錄,也就是第3頁的記錄了。
在這里,微軟提供了Skip和Tabke函數(shù),讓我們做起分頁來,得心應手。
接下來是this.AddRange()函數(shù),可以把批量的數(shù)據(jù)放到當前集合中(因為NewList本身就是一個集合)。
代碼的后半部分是兩個屬性:
- public bool HasPreviousPage
- {
- get { return (PageIndex > 0); }
- }
- public bool HasNextPage
- {
- get { return (PageIndex + 1 < AllPages); }
- }
這兩個屬性是為頁面顯示“上一頁”和“下一頁”,因為當我們顯示第一頁的時候,“上一頁”是不需要顯示的,如果最后一頁,是沒有“下一頁“的,所以在這里定義了兩個屬性,來判斷是否有上一頁和下一頁。
先看HasPreviousPage屬性,如果PageIndex是大于0的,說明不是在第一頁,所以就反回true,如果小于等于0(在這里小于0是沒有意思義的,因為頁面的索引值最小是0),說明是第一頁,所以返回是false。
再看HasNextPage屬性,因為頁面索引值的最大數(shù),與頁面的最大數(shù)差1(索引從0開始的原因),所以當PageIndex+1小于AllPages時,說沒有沒到最后一頁,返回值是true,如果PageIndex+1大于等于AllPages時(大于也沒有意義),說明是最后一頁,返回值為false。
4、 添加NewsController。選中Controller,右鍵添加一個NewsController 的Controller。
代碼如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.Mvc.Ajax;
- using MvcCompany.Models;
- using System.Configuration;
- namespace MvcCompany.Controllers
- {
- public class NewsController : Controller
- {
- DataClassesDataContext DCDC;
- int pageSize;
- public NewsController()
- {
- DCDC = new DataClassesDataContext();
- pageSize = Convert.ToInt32(ConfigurationManager.AppSettings["pagesize"]); //每個頁面的數(shù)量存放在web.config的appsetting里的pagesize節(jié)中,值為5
- }
- public ActionResult Index(int? page)
- {
- var NewsList = DCDC.news.Select(newss=>newss);
- var paginatedNews = new NewList<news>(NewsList, page ?? 0, pageSize); //實現(xiàn)分頁功能
- return View(paginatedNews);
- }
- [AcceptVerbs(HttpVerbs.Post)]
- public ActionResult Index(FormCollection formValues)
- {
- int? index = int.Parse(formValues.GetValue("pageindex").AttemptedValue);
- int page = index ??0 ;
- var NewsList = DCDC.news.Select(newss => newss);
- var paginatedNews = new NewList<news>(NewsList, page, pageSize); //實現(xiàn)分頁功能
- return View(paginatedNews);
- }
- }
- }
關(guān)于兩個Index重載,我們在設(shè)計完Views再討論。
5、 添加View。選中NewsController,右鍵,添加View,會彈出如下圖:
在這里,我們選中MvcCompany.Models.news,在View content下拉列表中選擇List(因為要實現(xiàn)列表分頁)。
Index.aspx頁面代碼如下:
- <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcCompany.Models.NewList<MvcCompany.Models.news>>" %>
- <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
- 新聞
- </asp:Content>
- <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
- <!--前半部分-->
- <%using (Html.BeginForm())
- { %>
- <h2>
- 新聞列表</h2>
- <table>
- <tr>
- <th>
- 編號
- </th>
- <th>
- 題目
- </th>
- <th>
- 時間
- </th>
- <th>
- 內(nèi)容
- </th>
- </tr>
- <% foreach (var item in Model)
- { %>
- <tr>
- <td>
- <%= Html.Encode(item.ID)%>
- </td>
- <td>
- <%= Html.Encode(item.title)%>
- </td>
- <td>
- <%= Html.Encode(String.Format("{0:g}", item.datetimes))%>
- </td>
- <td>
- <%= Html.Encode(item.contents)%>
- </td>
- </tr>
- <% } %>
- </table>
- <hr />
- <!--后半部分-->
- <% =Html.RouteLink("首頁", "UpcomingNews", new { page = 0 })%>|
- <% if (Model.HasPreviousPage)
- {%>
- <% =Html.RouteLink("上一頁", "UpcomingNews", new { page = (Model.PageIndex - 1) })%>|
- <%} %>
- <% if (Model.HasNextPage)
- {%>
- <% =Html.RouteLink("下一頁", "UpcomingNews", new { page = (Model.PageIndex + 1) })%>|
- <%} %>
- <% =Html.RouteLink("尾頁", "UpcomingNews", new { page = Model.PageSize - 1 })%>
- 第
- <%=Html.TextBox("pageindex", Model.PageIndex, new {style="width:30px;"})%>頁<input
- type="submit" value="轉(zhuǎn)到" id="sub" />
- <%} %>
- </asp:Content>
代碼的前半部分,很好理解。
代碼的后半部分,我們來分析一下,首頁,是采用
Html.RouteLink(“首頁”,”UpcomingNews”,new{ page=0}),是要定位到名稱為”UpcomingNews”的路由,這個路由在Global.asax中,代碼如下:
- routes.MapRoute(
- "UpcomingNews",
- "News/page/{page}",
- new { controller = "News", action = "Index" });
路由的名稱是”UpcominNews”,Url格式是News/page/{page},News和page是url的一種格式,后面{page}是參數(shù),這個參數(shù)的名稱要與View中,后半部分的new{page=0}的page,以及NewsController中的方法Index(int? page)中的page都必需統(tǒng)一。如果有請求
News/page/2的url,實際上請求的是 news?page=2的頁網(wǎng)(路由在ASP.NET MVC官上有說明)。
首頁為中的page=0,最終會成為NesController中Index(int? page)方法,的實參,返回首頁數(shù)據(jù)(及數(shù)據(jù)庫表的前5條數(shù)據(jù))。
上一頁,在當前頁數(shù)PageIndex的基礎(chǔ)上減1。
下一頁,在當前頁數(shù)PageIndex的基礎(chǔ)上加1
(注:上面的加減1都不會越界,因為當顯示第一頁時,“上一頁”的超連接就不會顯示。當最后一頁時,“下一頁”的超連接就不會顯示)
尾頁同首頁。
現(xiàn)在的一個問題是“轉(zhuǎn)到”第幾頁的實現(xiàn),我們知道,轉(zhuǎn)到第幾頁的“幾”,得用戶輸入,所以這里用一個Html.TextBox來實現(xiàn),完全代碼是
- <%=Html.TextBox("pageindex", Model.PageIndex, new {style="width:30px;"})%>
最終會生成一個名稱為pageindex的intup,類型為Text,它的值始終為當前的索引頁值Model.PageIndex,其中有一個寬度的屬性new {style="width:30px;"}。
同時還有一個<input type="submit" value="轉(zhuǎn)到" id="sub" />用來向后參提交。因為代碼中有<form>表單,所以可以在NewsController中接收這個提交。
Index(FormCollection formValues)方法用來處理這個提交。這里會從formValues.GetValue("pageindex").AttemptedValue中午到客戶端用戶輸入“pageindex”文本框中的值。從而來完成定向。
至此,我們就用ASP.NET MVC實現(xiàn)了一個分頁功能,相比之下要難ASP.NET的GridView自動分頁好多。
在這個例子中,我們在Models層中提供了實體類及news表的集合類。在Controller層增加了NewsController類。在View層增加了News的view,從M-V-C角度,分別完成了對分頁的功能。
【編輯推薦】