2017年7月25日 星期二

GridView 自訂分頁

一般 GridView 可以交給自動分頁產生出分頁效果,只需要將 GridView 的屬性 AllowPaging 設為 true,再搭配取得資料的語法,即可完成。
GridView 會依據資料筆數 和 GridView.PageCount 自動計算出頁數。
  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3.     if (!IsPostBack)
  4.     {
  5.         GridView1.DataSource = GetData();
  6.         GridView1.DataBind();
  7.     }
  8. }
  9.  
  10. private DataTable GetData()
  11. {
  12.     // 讀取資料
  13. }
  14.  
  15. // 翻頁時改變 PageIndex 後重新綁定 GridView 的資料
  16. protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
  17. {
  18.     GridView gridview = (GridView)sender;
  19.     GridView.PageIndex = e.NewPageIndex;
  20.     GridView.DataSource = GetData();
  21.     GridView.DataBind();
  22. }
因為GridView其實是將全部的資料都載入後,只顯示某一頁的資訊出來,所以當讀取的資料太多時就會造成載入的間很久。


此時可以使用自訂分頁的方式,自訂分頁除了要將 GridView 的屬性 AllowPaging 設為 true 之外,還要將 GridView 的屬性 AllowCustomPaging 設為 true,然後再在程式碼中指定 GridView.VirtualItemCount (虛擬 GridView 總共有幾筆資料),然後 GridView 會依據 VirtualItemCount 和 GridView.PageCount 自動計算出頁數,具體做法大致如下:
  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3.     if (!IsPostBack)
  4.     {
  5.         // GridView.VirtualItemCount:虛擬 GridView 總共有幾筆資料
  6.         GridView1.VirtualItemCount = GetAllDataCount();
  7.         GridView1.DataSource = GetSpecPageData(0);
  8.         GridView1.DataBind();
  9.     }
  10. }
  11. private int GetAllDataCount()
  12. {
  13.     // 讀取全部資料的筆數
  14. }
  15.  
  16. /// 只讀取指定頁的資料
  17. private DataTable GetSpecPageData(int PageIndex)
  18. {
  19.     // 比方每頁要顯示 15 筆
  20.     // 讀取第一頁要顯示的資料,那就只讀取 TOP 15
  21.     // 或是若要讀取第三頁 31 到第 45 筆,則可以用 OFFSET 30 ROWS FETCH NEXT 15 ROWS ONLY
  22.     // 意思是是位移 30 筆之後,再往下取 15 筆資料
  23.    請參考MIS2000 Lab
  24. }
  25.  
  26. 翻頁時改變 PageIndex 後重新綁定 GridView 的資料
  27. protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
  28. {
  29.     GridView gridview = (GridView)sender;
  30.     GridView.PageIndex = e.NewPageIndex;
  31.     GridView.DataSource = GetSpecPageData(e.NewPageIndex);
  32.     GridView.DataBind();
  33. }
此時遇到的題目是 GridView 的每頁筆數不一致(比方某個欄位要固定只顯示幾種資料),所以除了上述的寫法之外,還要加上一點點修正,如下:

加上一個全域變數,用以儲存每頁的筆數資訊
Dictionary<int, int> dictCountPerPage;
  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3.     if (!IsPostBack)
  4.     {
  5.         dictPerPageCount = GetPerPageCount();
  6.         GridView1.PageCount = dictPerPageCount[0];  // 指定第一頁的筆數
  7.         // GridView.VirtualItemCount:計算虛擬 GridView 總共有幾筆資料(因為每頁筆數不一,所以以要顯示的該頁的筆數當基準,用以計算出虛擬總筆數,好產生頁數(碼)
  8.         GridView1.VirtualItemCount = GridView1.PageCount * dictPerPageCount.Count;
  9.         GridView1.DataSource = GetSpecPageData(0);
  10.         GridView1.DataBind();
  11.     }
  12. }
  13. /// 依需求記錄每頁要顯示的資料筆數
  14. /// Key: 頁碼, Value: 每頁到第幾筆資料
  15. private Dictionary<int, int> GetPerPageCount()
  16. {
  17.     // 讀取全部每頁的資料的筆數
  18.     // 例如:
  19.     // Dictionary<int, int> dict = new Dictionary<int, int>();
  20.     // dict.Add(0, 10); // 第 1 頁: 0~10 筆
  21.     // dict.Add(1, 16); // 第 2 頁: 11~16 筆    // dict.Add(2, 18); // 第 3 頁: 17~18 筆
  22.     // dict.Add(3, 22); // 第 4 頁: 19~22 筆
  23.     // Session.Add("PerPageCount", dictPerPageCount); 將分頁資訊記錄在 Session
  24.     // return dict;
  25. }
  26. /// 只讀取指定頁的資料
  27. private DataTable GetSpecPageData(int PageIndex)
  28. {
  29.     // 比方每頁要顯示 15 筆
  30.     // 讀取第一頁要顯示的資料,那就只讀取 TOP 15
  31.     // 或是若要讀取第三頁 31 到第 45 筆,則可以用 OFFSET 30 ROWS FETCH NEXT 15 ROWS ONLY
  32.     // 意思是是位移 30 筆之後,再往下取 15 筆資料
  33.     // SQL 語法:
  34.    StringBuilder SQL = new StringBuilder("...");
  35.     SQL.Append(" ORDER BY 1, 2, 3, 4");
  36.     // 取回第幾筆~第幾筆資料
  37.     if (CurrentPage == 0)
  38.         SQL.AppendFormat(" OFFSET {0} ROWS FETCH NEXT {1} ROWS ONLY", 0, dicRowCountPerPage[CurrentPage]);
  39.     else
  40.         SQL.AppendFormat(" OFFSET {0} ROWS FETCH NEXT {1} ROWS ONLY", dictCountPerPage[PageIndex- 1], dictCountPerPage[PageIndex] - dictCountPerPage[PageIndex- 1]);
  41.     // 每次都重新指定 PageCount 和 VirtualItemCount
  42.     if (PageIndex== 0)
  43.         GridView1.PageSize = dictCountPerPage[PageIndex];
  44.     else
  45.         GridView1.PageSize = dictCountPerPage[PageIndex] - dictCountPerPage[PageIndex - 1];
  46.     GridView1.VirtualItemCount = GridView1.PageSize * dictCountPerPage.Count;
  47. }
  48. /// 翻頁時改變 PageIndex 後重新綁定 GridView 的資料
  49. protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
  50. {
  51.     GridView gridview = (GridView)sender;
  52.     GridView.PageIndex = e.NewPageIndex;
  53.     if (dictCountPerPage == null)
  54.         dictCountPerPage = (Dictionary<int, int>)Session["PerPageCount"];
  55.     GridView.DataSource = GetSpecPageData(e.NewPageIndex);
  56.     GridView.DataBind();
  57. }