閱讀目錄
分頁關注的內容
前面博文中,通過自行構造HTML表格代碼,可以生成易於管理、易於擴展的數據列表。但在實際應用中,數據量動則成百上千,單一頁面根本無法完全顯示所有數據。把所有數據進行分頁后逐頁顯示,是當前比較流行的數據展示方式。因此,我們需要研究和表格方式展示數據相適應的分頁管理機制。
分頁顯示的核心,是根據頁面記錄數、頁號、查詢條件、排序順序等因素,在數據庫中查出該頁相對應的數據集(DataTable)。按頁返回數據集的功能,已經作為一個基本的功能被封裝到AccessDB中了,通過AccessDB.GetPage就可以直接獲取。因此,我們只需要關心執行查詢以外的各種管理和操作,包括以下方面:
- 獲取總記錄集的記錄數
- 頁碼的有效性的檢驗
- 查詢條件的傳遞和應用
- 向任意頁面跳轉的支持
- 分頁器的樣式控制
狀態的傳遞
分頁管理的目的就是要對多個相互關聯的頁面進行管理,這些頁面之間存在着聯系,因此必須要能夠把當前頁面的某些重要狀態傳遞給下一個頁面。這些狀態包括:
- 數據表名
- 當前頁號
- 頁面大小
- 當前查詢條件
- 當前排序條件
ASPX有多種方式在頁面間傳遞狀態,如Cookie、Session、URL參數等。其中,以URL參數進行狀態傳遞,是當前很多分頁工具的普遍做法。綜合考慮需求,使用URL傳遞參數,需要定義以下參數:
- TableName
- PageNo
- PageSize
- strWhere
- strOrder
分頁的管理在頁面中的體現就是分頁器,即一組鏈接按鈕和文字信息,通過它們可以了解當前分頁信息,進行各種分頁的跳轉。有了上述的參數定義后,就可以定義分頁的幾個主要鏈接的形式。和前面的表格生成工具相一致,我們把分頁器的HTML代碼生成也封裝到一個函數中:
public string GetPager(int pagecount, int currentpage, string urlpara) { string strPager = ""; strPager += "<a href='?PageNo=1" + urlpara + "'>首頁</a>"; strPager += " <a href='?PageNo=" + (currentpage - 1 < 1 ? 1 : currentpage - 1).ToString() + urlpara + "'>上頁</a>"; strPager += " " + currentpage.ToString() + "/" + pagecount.ToString(); strPager += " <a href='?PageNo=" + (currentpage + 1 > pagecount ? pagecount : currentpage + 1).ToString() + urlpara + "'>下頁</a>"; strPager += " <a href='?PageNo=" + pagecount.ToString() + urlpara + "'>末頁</a>"; return strPager; }
通過語句調用:
strContent += GetPager(20,1,"");
達到HTML結果如下:
<a href='?PageNo=1'>首頁</a> <a href='?PageNo=1'>上頁</a> 1/20 <a href='?PageNo=2'>下頁</a> <a href='?PageNo=20'>末頁</a>
頁面顯示效果如下:
對於分頁器的構造方法而言,需要的參數,就是PageNo而已。但如果頁面還有其他的參數,跳轉鏈接應該同時把這些參數傳遞給下一個頁面。因此,我們看到構造方法中有一個urlpara,就是用來傳遞其他的參數的。
數據的獲取
通過傳入的參數,就可以進行頁面數據的獲取了。獲取記錄的流程是:
取得參數——查詢總記錄——判斷范圍,修正頁碼——查詢記錄集——構造分頁器代碼
代碼如下:
protected void Page_Load(object sender, EventArgs e) { //獲取參數 int PageNo = Convert.ToInt32(Request.QueryString["PageNo"]); int PageSize = 10; //PageSize默認值為10 //獲取總記錄數 int RecordCount = AccessDB.GetCount("select * from book"); int PageCount = Convert.ToInt32(Math.Ceiling((double)RecordCount / PageSize)); //有效性判斷 if (PageNo < 1) PageNo = 1; if (PageNo > PageCount) PageNo = PageCount; //查詢數據 DataTable dt = AccessDB.GetPage("select * from book",PageSize,PageNo,"ID",false ); //表格 strContent = Formater.FullTable(dt, "ID", "tblsample", "|出版年|書號|", "ID=編號;出版單位=出版社;", true, true, "書名", "showbook.aspx?id=KeyField", "del.aspx", "edit.aspx"); //分頁器 strContent += GetPager(PageSize ,PageNo,""); }
效果顯示如下:
上面的分頁器的頁面大小PageSize被設置為10,可以修改參數或者改造成為任意指定的大小,非常簡單就不再詳述了。另外,查詢語句中,改用GetPage,同時sql語句不再限定條數了。
查詢結果的分頁
查詢是數據管理的一個常用功能,查詢瀏覽界面和數據瀏覽界面往往是同一個界面,或者說,查詢功能本身就是數據瀏覽的一部分。對於查詢的結果,也需要和瀏覽所有數據記錄一致,采用相同的模式進行操作。所以分頁管理必須要把查詢綜合考慮進來。
如果是查詢結果分頁瀏覽,和前面的分頁瀏覽有何不同呢?其實很容易看到,就是需要把上面的sql語句,增加一個查詢條件即可。因此,查詢分頁,實質上就是把查詢條件作為參數進行傳遞。
另外,既然把Where子句進行參數傳遞,順便把Order子句也進行傳遞,這樣sql的大多數需求就能夠滿足了。
在實際應用中,查詢輸入的條件往往是中文的,因此還需要對strWhere進行編碼和解碼,把中文轉換成為URL可以識別的UTF8編碼。
對於上面的Demo頁面,增加一個查詢對話框,如下:
查詢按鈕處理如下:
//查詢按鈕事件 protected void Button1_Click(object sender, EventArgs e) { strWhere = " and 書名 like '%" + TextBox1.Text + "%'"; SetContent(); }
由於按鈕事件的處理是在Page_Load之后,在Page_Load中已經對strContent進行了一次構造,這里必須重新構造strContent,所以把上面的構造方法放到一個單獨的函數SetContent中,在Page_Load中調用一次,這里再調用一次,進行覆蓋。
另外,參數strWhere是一個頁面級的變量,默認為空串,可以把參數保存起來,在調用SetContent時,取得這個參數並構造查詢串。
string strSQL = "select * from book"; if (strWhere == "") //沒有最近的查詢動作,檢查有沒有傳遞的參數 { if (Request.QueryString["strWhere"] != null) strWhere =HttpUtility.UrlDecode( Request.QueryString["strWhere"].ToString()); } strSQL += " where 1=1" + strWhere; //... //分頁器 strContent += GetPager(PageCount, PageNo, "&strWhere="+HttpUtility.UrlEncode(strWhere));
上面代碼中,strWhere有可能有多種情況:
- 默認方式打開,為空串
- 單擊查詢后的處理,為剛才設定的條件
- 從分頁器的鏈接傳遞而來,但是優先級要低於查詢的條件
下圖是查詢“名著”的結果,進行分頁瀏覽的第四頁,下方狀態欄是鼠標放到“下頁”的鏈接信息:
跳頁的實現
除了按順序瀏覽,很多頁面數量往往非常巨大,因此分頁器還需要支持跳頁,可以向任意頁面跳轉。由於通過URL參數進行控制,實際非常簡單,參數一修改即可。實現則需要通過js來實現。
strPager += "<input name='GotoNum' type='text' id='GotoNum'/><a href=\"#\" onclick=\"var url=document.URL;var gn=document.getElementById('GotoNum').value;if(gn!=''){if(url.indexOf('PageNo=')==-1){url=url +(url.indexOf('?')==-1?'?':'&')+ 'PageNo='+gn;}else{ url=url.replace('PageNo=', 'PageNo='+gn+'&wy='); }window.navigate(url);}\" >跳轉</a>";
這樣構造出的分頁器就具備了跳轉功能了!
分頁器的樣式
分頁功能幾乎是每個數據管理頁面都需要的,但其樣式總的來說,不會有太多的變化,因此,寫好一個通用性較強的樣式,就可以到處使用了。這是一個常用的分頁器樣式。
<style type="text/css""> /** 分頁 **/ .pager{width:770px;text-align:center;margin:10px 0 10px 0;position:absolute;bottom:0px;} .pager a{display:inline-block;font-size:12px; width:40px; background-color:#FFFFFF;padding:3px;text-align:center;border:1px #CCCCCC dotted;margin:0px 0px 2px 2px;} .pager a:link,a:visited{ text-decoration:none;} .pager a:hover{text-decoration:underline; background-color:#FF99FF;} .pager #GotoNum{width:30px;} .pager #reccount,.pager #pageinfo{display:inline-block;margin:0 5px 3px 5px;font-weight:bold;} </style>
加上這個分頁器后,將頁面代碼放到.pager的div中,效果如下:
頁面的完整的處理流程
以下是頁面的完成處理流程代碼:
public partial class book_pager_query : System.Web.UI.Page { protected string strContent; string strWhere=""; protected void Page_Load(object sender, EventArgs e) { if(!Page.IsPostBack) SetContent(); } private void SetContent() { string strSQL = "select * from book"; if (strWhere == "") //沒有最近的查詢動作,檢查有沒有傳遞的參數 { if (Request.QueryString["strWhere"] != null) strWhere =HttpUtility.UrlDecode( Request.QueryString["strWhere"].ToString()); } strSQL += " where 1=1" + strWhere; //獲取參數 int PageNo = Convert.ToInt32(Request.QueryString["PageNo"]); int PageSize = 10; //PageSize默認值為10 //獲取總記錄數 int RecordCount = AccessDB.GetCount(strSQL); int PageCount = Convert.ToInt32(Math.Ceiling((double)RecordCount / PageSize)); //有效性判斷 if (PageNo < 1) PageNo = 1; if (PageNo > PageCount) PageNo = PageCount; //查詢數據 DataTable dt = AccessDB.GetPage(strSQL, PageSize, PageNo, "ID", false); //表格 strContent = Formater.FullTable(dt, "ID", "tblsample", "|出版年|書號|", "ID=編號;出版單位=出版社;", true, true, "書名", "showbook.aspx?id=KeyField", "del.aspx", "edit.aspx"); //分頁器 strContent +="<div class=pager>"+ GetPager(PageCount, PageNo, "&strWhere="+HttpUtility.UrlEncode(strWhere))+"</div>"; } public string GetPager(int pagecount, int currentpage, string urlpara) { string strPager = ""; strPager += "<a href=\"?PageNo=1" + urlpara + "\">首頁</a>"; strPager += " <a href=\"?PageNo=" + (currentpage - 1 < 1 ? 1 : currentpage - 1).ToString() + urlpara + "\">上頁</a>"; strPager += " " + currentpage.ToString() + "/" + pagecount.ToString(); strPager += " <a href=\"?PageNo=" + (currentpage + 1 > pagecount ? pagecount : currentpage + 1).ToString() + urlpara + "\">下頁</a>"; strPager += " <a href='?PageNo=" + pagecount.ToString() + urlpara + "'>末頁</a>"; strPager += "<input name='GotoNum' type='text' id='GotoNum'/><a href=\"#\" onclick=\"var url=document.URL;var gn=document.getElementById('GotoNum').value;if(gn!=''){if(url.indexOf('PageNo=')==-1){url=url +(url.indexOf('?')==-1?'?':'&')+ 'PageNo='+gn;}else{ url=url.replace('PageNo=', 'PageNo='+gn+'&wy='); }window.navigate(url);}\" >跳轉</a>"; return strPager; } //查詢按鈕事件 protected void Button1_Click(object sender, EventArgs e) { strWhere = " and 書名 like '%" + TextBox1.Text + "%'"; SetContent(); } }
GetPager的通用性較強,所以已經集成到CommonCode中了,通過Pager對象可以訪問。