1. 引言
在ASP.NET中DataList可以實現資料展示,我們可以通過定製其模版實現豐富的格式,但是美中不足的時DataList預設情況下不支援分頁,我們當然可以編寫一個使用者控制項以實現分頁功能,但是這種方案仍然不是很好,我們希望像使用普通ASP.NET伺服器端控制項一樣,只需要放置一個DataList並設定分頁樣式就可以輸出分頁連結。
在上次任務中我們建立了DataPager類將建立分頁的操作從GridView分離出來,本次任務將嘗試重用DataPager類為DataList增加分頁特性。
2. 分析
開發自訂GridView控制項時,可以通過向控制項中加入具有特定CommandName的按鈕實現分頁,但是對於DataList卻不適用,因為DataList不能接收到用戶端的回傳事件,這也是DataList類和GridView類的一個區別—DataList類沒有實現IPostBackEventHandler介面。為了能夠使DataList接收用戶端回傳並觸發分頁事件,需要使自訂DataList實現IPostBackEventHandler介面,並使用自訂事件參數類在觸發事件時傳遞頁碼資訊。
IPostBackEventHandler介面定義了ASP.NET伺服器控制項為處理回傳事件而必須實現的方法,它的成員只有一個方法:
void RaisePostBackEvent(string eventArgument)
該方法由類實現時,使伺服器控制項能夠處理將表單發送到伺服器時引發的事件。
接下來需要考慮如何在用戶端引起回傳事件,即怎樣產生回傳指令碼。這裡使用到了ClientScriptManager類,該類作為Page類的一個屬性ClientScript出現,通過調用該類的GetPostBackClientHyperlink方法產生用戶端指令碼以引起回傳,該方法有兩個形式的重載:
- GetPostBackClientHyperlink (Control, String)
擷取一個引用,並在其開頭附加 javascript:,可以在用戶端事件中使用該引用,並將該引用與指定的事件參數一起使用,以便回傳到指定控制項的伺服器。
- GetPostBackClientHyperlink (Control, String, Boolean)
擷取一個引用,並在其開頭附加 javascript:,該引用可用於在用戶端事件中回傳到指定控制項的伺服器,回傳時使用指定的事件參數和一個指示是否為事件驗證註冊該回傳的布爾值。
其中第一個參數指明了處理回傳的伺服器控制項,第二個參數代表傳遞給伺服器控制項的參數,第三個參數代表是否驗證註冊回傳事件。
接下來編寫實現代碼。
3. 實現
3.1 建立DataListPager類,該類繼承自DataPager類實現了為分頁連結添加回傳指令碼操作:
public class DataListPager : DataPager{ public DataListPager(PagerSettings setting, int pageIndex, int recordCount, int pageSize, DataList dal) : base(setting, pageIndex, recordCount, pageSize) { LinkButton btn = null; int _pageCount = recordCount % pageSize == 0 ? recordCount / pageSize : recordCount / pageSize + 1; int index; foreach (Control control in this.Controls) { if (control is LinkButton) { btn = (LinkButton)control; if (btn.Enabled) { string argvalue = btn.CommandArgument; bool isInt = int.TryParse(argvalue, out index); string arg = string.Empty; if (isInt) { index--; } else { switch (argvalue) { case Constant.FIRST_PAGE: index = 0; break; case Constant.PREV_PAGE: index = pageIndex - 1 < 0 ? 0 : pageIndex - 1; break; case Constant.NEXT_PAGE: index = pageIndex + 1 > _pageCount - 1 ? _pageCount - 1 : pageIndex + 1; break; case Constant.LAST_PAGE: index = _pageCount - 1; break; } } arg = Constant.PAGE_ARGUMENT + Constant.ARGUMENT_SPLITTER + index; btn.Attributes.Add(HtmlTextWriterAttribute.Href.ToString(), dal.Page.ClientScript.GetPostBackClientHyperlink(dal, arg)); } } } }}
3.2 建立DataList類,繼承自預設的DataList類並實現IPostBackEventHandler介面:
[ToolboxData(@"<{0}:DataList runat='server'></{0}:DataList>")][ParseChildren(true)][PersistChildren(false)]public class DataList : System.Web.UI.WebControls.DataList, IPostBackEventHandler{}
3.3 定義DataList屬性,儲存分頁設定資訊:
public int RecordCount{ get { object o = ViewState["RecordCount"]; return o == null ? 0 : Convert.ToInt32(o); } set { ViewState["RecordCount"] = value; }}public virtual int PageIndex{ get { object o = ViewState["PageIndex"]; return o == null ? 0 : Convert.ToInt32(o); } set { ViewState["PageIndex"] = value; }}[DefaultValue(10)]public virtual int PageSize{ get { object o = ViewState["PageSize"]; return o == null ? 10 : Convert.ToInt32(o); } set { ViewState["PageSize"] = value; }}private PagerSettings _settings;[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)][PersistenceMode(PersistenceMode.InnerProperty)]public PagerSettings PagerSettings{ get { if (_settings == null) _settings = new PagerSettings(); if (base.IsTrackingViewState) ((IStateManager)_settings).TrackViewState(); return this._settings; }}public bool EnablePaging{ get { object o = ViewState["EnablePaging"]; return o == null ? false : Convert.ToBoolean(o); } set { ViewState["EnablePaging"] = value; }}
3.4 建立自訂事件類別,儲存新頁碼:
public class DataListPageEventArgs : EventArgs{ public int NewPageIndex { get; set; }}
3.5 在DataList類中建立事件和輔助方法:
public event EventHandler<DataListPageEventArgs> PageIndexChanging;protected virtual void OnPageIndexChanging(object sender, DataListPageEventArgs e){ if (PageIndexChanging != null) { PageIndexChanging(sender, e); }}
3.6 編寫RenderContent方法,該方法是實現了分頁的核心操作,通過執行個體化DataListPager類的執行個體,將觸發回傳操作的連結加入到當前DataList中:
protected override void RenderContents(HtmlTextWriter writer){ this.RenderBeginTag(writer); if (EnablePaging) { Table table = new Table(); TableRow row = new TableRow(); table.Rows.Add(row); TableCell cell = new TableCell(); row.RenderBeginTag(writer); cell.RenderBeginTag(writer); base.RenderContents(writer); cell.RenderEndTag(writer); row.RenderEndTag(writer); TableRow rowpager = new TableRow(); DataListPager pager = new DataListPager(PagerSettings, PageIndex, RecordCount, PageSize, this); rowpager.Cells.Add(pager); rowpager.RenderBeginTag(writer); pager.RenderControl(writer); rowpager.RenderEndTag(writer); } else { base.RenderContents(writer); } this.RenderEndTag(writer);}
3.7 編寫IPostEventHandler介面中RaisePostBackEvent方法的實現,判斷當前事件參數並觸發換頁事件:
public void RaisePostBackEvent(string eventArgument){ string[] args = eventArgument.Split(Constant.ARGUMENT_SPLITTER); if (args == null || args.Length < 1) return; string name = args[0]; if (name == Constant.PAGE_ARGUMENT) { int index = 0; string argvalue = args[1]; bool isInt = int.TryParse(argvalue, out index); if (isInt) { DataListPageEventArgs arg = new DataListPageEventArgs(); arg.NewPageIndex = index; OnPageIndexChanging(this, arg); } }}
3.8 重寫SaveViewState和LoadViewState方法定義儲存和閱讀檢視狀態方法:
protected override object SaveViewState(){ object o = base.SaveViewState(); object osetting = ((IStateManager)PagerSettings).SaveViewState(); Pair p = new Pair(o, osetting); return p;}protected override void LoadViewState(object savedState){ if (savedState != null) { Pair p = (Pair)savedState; base.LoadViewState(p.First); ((IStateManager)PagerSettings).LoadViewState(p.Second); } else { base.LoadViewState(null); }}
3.9 在網站中建立測試頁,聲明並定義自訂DataList:
<cc:DataList ID="dalData" runat="server" EnablePaging="true" PageSize="10" OnPageIndexChanging="dalData_PageIndexChanging" PagerSettings-FirstPageText="????"> <ItemTemplate> <table class="data" cellpadding="0" cellspacing="1" border="0"> <tr> <td><asp:Label ID="lblId" runat="server" Text='<%#Eval("Id") %>'></asp:Label></td> <td><asp:Label ID="lblName" runat="server" Text='<%#Eval("Name") %>'></asp:Label></td> </tr> </table> </ItemTemplate></cc:DataList>
3.10 在後置代碼中編寫BindData方法載入資料,並且在頁面不是回傳時調用此方法顯示資料:
protected void Page_Load(object sender, EventArgs e){ if (!IsPostBack) this.BindData();}private void BindData(){ DataTable table = new DataTable(); DataColumn col = new DataColumn("Id"); table.Columns.Add(col); col = new DataColumn("Name"); table.Columns.Add(col); for (int i = dalData.PageIndex * dalData.PageSize; i < dalData.PageIndex * dalData.PageSize+dalData.PageSize; i++) { DataRow row = table.NewRow(); row[0] = i; row[1] = "人員 " + i; table.Rows.Add(row); } dalData.DataSource = table; dalData.RecordCount = 30; dalData.DataBind();}
3.11 編寫DataList的切換頁事件,將新的頁碼索引賦值給DataList並執行資料繫結:
protected void dalData_PageIndexChanging(object sender, DataListPageEventArgs e){ dalData.PageIndex = e.NewPageIndex; this.BindData();}
3.12 在瀏覽器中預覽效果:
4. 總結
在本次任務中,通過為LinkButton加入了JavaScript指令碼使得在用戶端點擊時可以引起回傳操作,這是通過ClientScriptManager類的GetPostBackClientHyperlink方法實現的。引起提交後,為了能夠在伺服器端處理回傳事件,在自訂DataList控制項中實現了IPostBackEventHandler介面並在實現方法中調用了自訂頁切換事件,使開發人員能夠根據新頁碼進行資料繫結。可以看到,現在DataList和GridView都已經實現分頁了,但是從某種意義上來說,這個解決方案還不夠優雅(經常被Java程式員拿來說事的一個詞-_-!!),您可以自行加以改良。
ASP.NET自訂控制項系列文章
前言
第一天 簡單的星級控制項
第二天 帶有自訂樣式的星級控制項
第三天 使用控制項狀態的星級控制項
第四天 摺疊面板自訂控制項
第五天 可以評分的星級控制項
第六天 可以綁定資料來源的星級控制項
第七天 開發具有豐富特性的清單控制項
第八天 顯示多個條目星級評等的資料繫結控制項
第九天 自訂GridView
第十天 實現分頁功能的DataList
全部源碼下載
本系列文章PDF版本下載