1. 引言
ASP.NET 2.0裡為我們提供了全新的GridView控制項,它在DataGrid基礎之上增加了許多新的特性,例如不編寫一行代碼就可以和資料來源控制項結合起來實現資料的展示並且分頁,但是這種分頁效率較低,這是從資料庫一次讀取所有的資料再進行分頁,如果資料量較少則可以實現快速開發,但是假如資料庫中存放大量資料,這種操作效能顯得就比較低了,所以我們一般自己編寫資料讀取方法,在PageIndexChanging事件中綁定新頁,這時會遇到一個問題,如果我們僅讀取一頁資料時GridView不能正確計算出共有多少頁,也就無法正確呈現出分頁按鈕,因此需要考慮對其進行擴充。另外DataList控制項提供了靈活的模版設定以顯示記錄內容,但是它有一個最大的弱點就是不支援分頁,我們同樣試圖擴充DataList以增加分頁的特性。
2. 分析
之所以要將GridView和DataList放在一起考慮,是因為不論哪一個資料繫結控制項產生分頁連結清單時需要執行相似的操作。GridView控制項本身支援分頁,所以在開發自訂表格格控制項時,只需要加入相應的按鈕服器端控制項,將CommandName屬性設定為Page,並設定CommandArgument屬性為特定值,即可由GridView捕捉到頁面變更事件,為了避免在代碼中出現“魔法字元”,定義了常量類儲存使用的字串常量。但是這種方法對於DataList卻不適用,因為DataList不能接收到用戶端的回傳事件,這也是DataList類和GridView類的一個區別—DataList類沒有實現IPostBackEventHandler介面。為了能夠使DataList接收用戶端回傳並觸發分頁事件,需要使自訂DataList實現IPostBackEventHandler介面,並使用自訂事件參數類在觸發事件時傳遞頁碼資訊。
現在自訂GridView和DataList控制項均可以實現分頁了,為了使兩者有一個統一的分頁外觀,定義分頁基類實現分頁功能,並且針對DataList分頁,繼承分頁基類並設定分頁按鈕的回傳指令碼。最後為了能夠把分頁按鈕作為一個整體添加到表格中,使之繼承自TableCell(表格中的儲存格)。
3. 實現
3.1 建立儲存命令字串的常量類Constant
public class Constant{ public const string FIRST_PAGE = "First"; public const string PREV_PAGE = "Prev"; public const string NEXT_PAGE = "Next"; public const string LAST_PAGE = "Last"; public const string PAGE_ARGUMENT = "Page"; public const char ARGUMENT_SPLITTER = '$';}
分頁按鈕中的CommandArgument屬性設定如下:
屬性 |
描述 |
First |
移動到第一頁 |
Last |
移動到最後一頁 |
Prev |
移動到上一頁 |
Next |
移動到下一頁 |
一個數字 |
移動到某個特定頁 |
3.2 定義DataPager類以產生分頁連結,該類繼承自TableCell類,並且定義了私人變數以計算頁碼(PagerSettings類是.NET預定義類,儲存了有關分頁設定的資訊)
public class DataPager : TableCell{ private int _pageIndex; private int _recordCount; private int _pageSize; private int _pageCount; private PagerSettings _settings;}
3.3 定義建構函式,接收當前頁索引、總記錄數、頁大小資訊並根據這些資料計算出總頁碼,接著調用GeneratePage方法產生分頁
public DataPager(PagerSettings setting, int pageIndex, int recordCount, int pageSize){ _settings = setting; _pageIndex = pageIndex; _recordCount = recordCount; _pageSize = pageSize; _pageCount = _recordCount % _pageSize == 0 ? _recordCount / _pageSize : _recordCount / _pageSize + 1; GeneratePage();}
3.3 編寫GeneratePage方法,根據PagerSetting設定產生帶有數字或不帶有數位分頁連結:
private void GeneratePage(){ if (_settings.Mode == PagerButtons.NextPrevious || _settings.Mode == PagerButtons.NextPreviousFirstLast) { GeneratePrevNextPage(); } else if (_settings.Mode == PagerButtons.Numeric || _settings.Mode == PagerButtons.NumericFirstLast) { GenerateNumericPage(); }}
3.4 編寫產生兩類分頁連結的方法:
private void GeneratePrevNextPage(){ GeneratePage(false);} private void GenerateNumericPage(){ GeneratePage(true);}
3.5 產生分頁時均調用了GeneratePage方法,該方法建立了一系列的LinkButton並設定了CommandArgument和CommandName屬性,以下是該方法的實現:
private void GeneratePage(bool generateNumber){ this.Controls.Add(new LiteralControl(" ")); if (_recordCount > 0) { this.Controls.Add(new LiteralControl("?? ")); this.Controls.Add(new LiteralControl(_recordCount.ToString())); this.Controls.Add(new LiteralControl(" ?????? ???? ")); this.Controls.Add(new LiteralControl(_pageSize.ToString())); this.Controls.Add(new LiteralControl(" ?????? ")); } this.Controls.Add(new LiteralControl((_pageIndex + 1).ToString())); this.Controls.Add(new LiteralControl("/")); this.Controls.Add(new LiteralControl(_pageCount.ToString())); this.Controls.Add(new LiteralControl(" ")); LinkButton btnFrist = new LinkButton(); LinkButton btnPrev = new LinkButton(); LinkButton btnNext = new LinkButton(); LinkButton btnLast = new LinkButton(); if (!String.IsNullOrEmpty(_settings.FirstPageImageUrl)) { btnFrist.Text = "<img src='" + ResolveUrl(_settings.FirstPageImageUrl) + "' border='0'/>"; } else { btnFrist.Text = _settings.FirstPageText; } btnFrist.CommandName = Constant.PAGE_ARGUMENT; btnFrist.CommandArgument = Constant.FIRST_PAGE; btnFrist.Font.Underline = false; if (!String.IsNullOrEmpty(_settings.PreviousPageImageUrl)) { btnPrev.Text = "<img src='" + ResolveUrl(_settings.PreviousPageImageUrl) + "' border='0'/>"; } else { btnPrev.Text = _settings.PreviousPageText; } btnPrev.CommandName = Constant.PAGE_ARGUMENT; btnPrev.CommandArgument = Constant.PREV_PAGE; btnPrev.Font.Underline = false; if (!String.IsNullOrEmpty(_settings.NextPageImageUrl)) { btnNext.Text = "<img src='" + ResolveUrl(_settings.NextPageImageUrl) + "' border='0'/>"; } else { btnNext.Text = _settings.NextPageText; } btnNext.CommandName = Constant.PAGE_ARGUMENT; btnNext.CommandArgument = Constant.NEXT_PAGE; btnNext.Font.Underline = false; if (!String.IsNullOrEmpty(_settings.LastPageImageUrl)) { btnLast.Text = "<img src='" + ResolveUrl(_settings.LastPageImageUrl) + "' border='0'/>"; } else { btnLast.Text = _settings.LastPageText; } btnLast.CommandName = Constant.PAGE_ARGUMENT; btnLast.CommandArgument = Constant.LAST_PAGE; btnLast.Font.Underline = false; if (this._pageIndex <= 0) { btnFrist.Enabled = btnPrev.Enabled = false; } else { btnFrist.Enabled = btnPrev.Enabled = true; } this.Controls.Add(btnFrist); this.Controls.Add(new LiteralControl(" ")); this.Controls.Add(btnPrev); this.Controls.Add(new LiteralControl(" ")); if (generateNumber) { int rightCount = (int)(_settings.PageButtonCount / 2); int leftCount = _settings.PageButtonCount % 2 == 0 ? rightCount - 1 : rightCount; for (int i = 0; i < _pageCount; i++) { if (_pageCount > _settings.PageButtonCount) { if (i < _pageIndex - leftCount && _pageCount - 1 - i > _settings.PageButtonCount - 1) { continue; } else if (i > _pageIndex + rightCount && i > _settings.PageButtonCount - 1) { continue; } } if (i == _pageIndex) { this.Controls.Add(new LiteralControl("<span style='color:red;font-weight:bold'>" + (i + 1).ToString() + "</span>")); } else { LinkButton lb = new LinkButton(); lb.Text = (i + 1).ToString(); lb.CommandName = Constant.PAGE_ARGUMENT; lb.CommandArgument = (i + 1).ToString(); this.Controls.Add(lb); } this.Controls.Add(new LiteralControl(" ")); } } if (this._pageIndex >= _pageCount - 1) { btnNext.Enabled = btnLast.Enabled = false; } else { btnNext.Enabled = btnLast.Enabled = true; } this.Controls.Add(btnNext); this.Controls.Add(new LiteralControl(" ")); this.Controls.Add(btnLast); this.Controls.Add(new LiteralControl(" "));}
3.6 建立自訂GridView類,該類繼承自ASP.NET中預設的GridView(System.Web.UI.WebControls.GridView):
[ToolboxData(@"<{0}:GridView runat='server'></{0}:GridView>")]public class GridView : System.Web.UI.WebControls.GridView{}
3.7 定義GridView屬性,新增了RecordCound儲存總記錄數,並且重寫了PageIndex和PageCount屬性以儲存當前頁索引和總頁數,此外還定義了MouseOverBackgroundColor屬性以實現當滑鼠移到資料行上時背景變色。
public int RecordCount{ get { object o = ViewState["RecordCount"]; return o == null ? 0 : Convert.ToInt32(o); } set { ViewState["RecordCount"] = value; }} public override int PageIndex{ get { object o = ViewState["PageIndex"]; return o == null ? 0 : Convert.ToInt32(o); } set { ViewState["PageIndex"] = value; }} public override int PageCount{ get { int pageCount = RecordCount % PageSize == 0 ? RecordCount / PageSize : RecordCount / PageSize + 1; return pageCount; }} public Color MouseOverBackgroundColor{ get { object o = ViewState["MouseOverBackgroundColor"]; return o == null ? Color.Silver : (Color)o; } set { ViewState["MouseOverBackgroundColor"] = value; }}
3.8 重寫OnRowCreated方法,在建立資料行時首先增加背景變色JavaScript,接下來根據分頁資訊建立頁碼連結儲存格添加到表格中。
protected override void OnRowCreated(GridViewRowEventArgs e){ if (e.Row.RowType == DataControlRowType.DataRow) { e.Row.Attributes.Add("onmouseover", "bgcolor=this.style.backgroundColor;this.style.backgroundColor='" + ColorTranslator.ToHtml(MouseOverBackgroundColor) + "';"); e.Row.Attributes.Add("onmouseout", "this.style.backgroundColor=bgcolor;"); } if (e.Row.RowType == DataControlRowType.Pager) { e.Row.Controls.Clear(); TableCell tc = new DataPager(PagerSettings, PageIndex, RecordCount, PageSize); tc.ColumnSpan = this.Columns.Count; e.Row.Controls.Add(tc); } base.OnRowCreated(e);}
3.9 為了實現在刪除最後一行資料時能夠滾動到上一頁,重寫了OnRowDeleted方法:
protected override void OnRowDeleting(GridViewDeleteEventArgs e){ if (this.Rows.Count == 1) this.PageIndex = this.PageIndex - 1 >= 0 ? this.PageIndex - 1 : 0; base.OnRowDeleting(e);}
3.10 在網站中聲明並定義自訂GridView:
<cc:GridView ID="gdvData" runat="server" PageSize="4" AllowPaging="True" OnPageIndexChanging="gdvData_PageIndexChanging" RecordCount="0" DataKeyNames="ID" Font-Size="12px" OnRowDeleting="gdvData_RowDeleting" Width="500px" AutoGenerateColumns="False" BackColor="White" BorderColor="#FFE0C0" BorderStyle="None" BorderWidth="1px" CellPadding="4" MouseOverBackgroundColor="255, 224, 192" OnRowDeleted="gdvData_RowDeleted" > <Columns> <asp:BoundField DataField="ID" HeaderText="????" /> <asp:BoundField DataField="Name" HeaderText="????" /> <asp:BoundField DataField="Favor" HeaderText="????" /> <asp:CommandField ShowDeleteButton="True" HeaderText="????" /> </Columns> <PagerSettings PageButtonCount="5" /> <RowStyle BackColor="White" ForeColor="#330099" /> <FooterStyle BackColor="#FFFFCC" ForeColor="#330099" /> <PagerStyle BackColor="#FFFFCC" ForeColor="#330099" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="#663399" /> <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="#FFFFCC" /> <AlternatingRowStyle BackColor="Yellow" /></cc:GridView>
3.11 編寫後置代碼以綁定到資料來源:
protected void Page_Load(object sender, EventArgs e){ if (!IsPostBack) this.BindData();} private void BindData(){ UserQuery query=null; object o = ViewState["Query"]; if (o == null) query = new UserQuery(); else query = (UserQuery)o; int total=0; IList<User> users = Users.GetUsers(query, gdvData.PageIndex, gdvData.PageSize, out total); gdvData.RecordCount = total; gdvData.DataSource = users; gdvData.DataBind(); }
3.12 編寫PageIndexChanging和RowDeleting事件處理常式:
protected void gdvData_PageIndexChanging(object sender, GridViewPageEventArgs e){ gdvData.PageIndex = e.NewPageIndex; this.BindData(); } protected void gdvData_RowDeleting(object sender, GridViewDeleteEventArgs e){ int id=Convert.ToInt32(gdvData.DataKeys[e.RowIndex].Value); Users.DeleteUser(id); this.BindData();}
3.13 在瀏覽器中預覽頁面(此代碼中的Users和User類均未給出,讀者可嘗試自行編寫,其中Users類相當於一個邏輯類實現了使用者的查詢和刪除,User類代表使用者實體)。
4. 總結
本次任務中建立了可以自動實現分頁的GridView控制項,把產生分頁連結的操作單獨提取出來建立了DataPager類,在此類中使用LinkButton並設定了CommandName和CommandArgument以實現換頁。下一次任務我們將在此類的基礎上為DataList添加分頁特性。
ASP.NET自訂控制項系列文章
前言
第一天 簡單的星級控制項
第二天 帶有自訂樣式的星級控制項
第三天 使用控制項狀態的星級控制項
第四天 摺疊面板自訂控制項
第五天 可以評分的星級控制項
第六天 可以綁定資料來源的星級控制項
第七天 開發具有豐富特性的清單控制項
第八天 顯示多個條目星級評等的資料繫結控制項
第九天 自訂GridView
第十天 實現分頁功能的DataList
全部源碼下載
本系列文章PDF版本下載