.NET 3.5中的DataPager碉堡了,可惜只支持ListView。传统的GridView和Repeater都无法直接使用DataPager分页。但我们如果稍加改造,就可以让Repeater和GridView支持DataPager分页。本网站的博客和留言板就是用Repeater+DataPager做的分页。
改造办法是自己写一个控件,让它继承GridView或Repeater,并实现IPageableItemContainer 接口。下面要发的是国外某高手写的代码,测试有效。具体使用的时候,要建一个类库项目,把代码编译成dll后,就可以添加到VS的工具箱里了!
一、自定义Repeater
using System.Web.UI; using System.Web.UI.WebControls; namespace WYJ.Web.Controls { /// <summary> /// Repeater with support for DataPager /// </summary> [ToolboxData("<{0}:DataPagerRepeater runat=server PersistentDataSource=true></{0}:DataPagerRepeater>")] public class DataPagerRepeater : Repeater, System.Web.UI.WebControls.IPageableItemContainer, INamingContainer { /// <summary> /// Number of rows to show /// </summary> public int MaximumRows { get { return ViewState["MaximumRows"] != null ? (int)ViewState["MaximumRows"] : -1; } } /// <summary> /// First row to show /// </summary> public int StartRowIndex { get { return ViewState["StartRowIndex"] != null ? (int)ViewState["StartRowIndex"] : -1; } } /// <summary> /// Total rows. When PagingInDataSource is set to true you must get the total records from the datasource (without paging) at the FetchingData event /// When PagingInDataSource is set to true you also need to set this when you load the data the first time. /// </summary> public int TotalRows { get { return ViewState["TotalRows"] != null ? (int)ViewState["TotalRows"] : -1; } set { ViewState["TotalRows"] = value; } } /// <summary> /// If repeater should store data source in view state. If false you need to get and bind data at post back. When using a connected data source this is handled by the data source. /// </summary> public bool PersistentDataSource { get { return ViewState["PersistentDataSource"] != null ? (bool)ViewState["PersistentDataSource"] : true; } set { ViewState["PersistentDataSource"] = value; } } /// <summary> /// Set to true if you want to handle paging in the data source. /// Ex if you are selecting data from the database and only select the current rows /// you must set this property to true and get and rebind data at the FetchingData event. /// If this is true you must also set the TotalRecords property at the FetchingData event. /// </summary> /// <seealso cref="FetchingData"/> /// <seealso cref="TotalRows"/> public bool PagingInDataSource { get { return ViewState["PageingInDataSource"] != null ? (bool)ViewState["PageingInDataSource"] : false; } set { ViewState["PageingInDataSource"] = value; } } /// <summary> /// Checks if you need to rebind data source at postback /// </summary> public bool NeedsDataSource { get { if (PagingInDataSource) return true; if (IsBoundUsingDataSourceID == false && !Page.IsPostBack) return true; if (IsBoundUsingDataSourceID == false && PersistentDataSource == false && Page.IsPostBack) return true; else return false; } } /// <summary> /// Loading ViewState /// </summary> /// <param name="savedState"></param> protected override void LoadViewState(object savedState) { base.LoadViewState(savedState); //if (Page.IsPostBack) //{ // if (!IsBoundUsingDataSourceID && PersistentDataSource && ViewState["DataSource"] != null) // { // this.DataSource = ViewState["DataSource"]; // this.DataBind(true); // } // if (IsBoundUsingDataSourceID) // { // this.DataBind(); // } //} } protected override void OnLoad(System.EventArgs e) { if (Page.IsPostBack) { if (NeedsDataSource && FetchingData != null) { if (PagingInDataSource) { SetPageProperties(StartRowIndex, MaximumRows, true); } FetchingData(this, null); } if (!IsBoundUsingDataSourceID && PersistentDataSource && ViewState["DataSource"] != null) { this.DataSource = ViewState["DataSource"]; this.DataBind(); } if (IsBoundUsingDataSourceID) { this.DataBind(); } } base.OnLoad(e); } /// <summary> /// Method used by pager to set totalrecords /// </summary> /// <param name="startRowIndex">startRowIndex</param> /// <param name="maximumRows">maximumRows</param> /// <param name="databind">databind</param> public void SetPageProperties(int startRowIndex, int maximumRows, bool databind) { ViewState["StartRowIndex"] = startRowIndex; ViewState["MaximumRows"] = maximumRows; if (TotalRows > -1) { if (TotalRowCountAvailable != null) { TotalRowCountAvailable(this, new PageEventArgs((int)ViewState["StartRowIndex"], (int)ViewState["MaximumRows"], TotalRows)); } } } /// <summary> /// OnDataPropertyChanged /// </summary> protected override void OnDataPropertyChanged() { if (MaximumRows != -1 || IsBoundUsingDataSourceID) { this.RequiresDataBinding = true; } base.OnDataPropertyChanged(); } /// <summary> /// Renders only current items selected by pager /// </summary> /// <param name="writer"></param> protected override void RenderChildren(HtmlTextWriter writer) { if (!PagingInDataSource && MaximumRows != -1) { foreach (RepeaterItem item in this.Items) { if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem) { item.Visible = false; if (item.ItemIndex >= (int)ViewState["StartRowIndex"] && item.ItemIndex < ((int)ViewState["StartRowIndex"] + (int)ViewState["MaximumRows"])) { item.Visible = true; } } else { item.Visible = true; } } } base.RenderChildren(writer); } /// <summary> /// Get Data /// </summary> /// <returns></returns> protected override System.Collections.IEnumerable GetData() { System.Collections.IEnumerable dataObjects = base.GetData(); if (dataObjects == null && this.DataSource != null) { if (this.DataSource is System.Collections.IEnumerable) dataObjects = (System.Collections.IEnumerable)this.DataSource; else dataObjects = ((System.ComponentModel.IListSource)this.DataSource).GetList(); } if (!PagingInDataSource && MaximumRows != -1 && dataObjects != null) { int i = -1; if (dataObjects != null) { i = 0; foreach (object o in dataObjects) { i++; } } ViewState["TotalRows"] = i; if (!IsBoundUsingDataSourceID && PersistentDataSource) ViewState["DataSource"] = this.DataSource; SetPageProperties(StartRowIndex, MaximumRows, true); } if (PagingInDataSource && !Page.IsPostBack) { SetPageProperties(StartRowIndex, MaximumRows, true); } return dataObjects; } /// <summary> /// Event when pager/repeater have counted total rows /// </summary> public event System.EventHandler<PageEventArgs> TotalRowCountAvailable; /// <summary> /// Event when repeater gets the data on postback /// </summary> public event System.EventHandler<PageEventArgs> FetchingData; } }
ASPX页面要做的事情(以我网站的留言板为例):
首先得把标签注册进来
<%@ Register Assembly="WYJ.Web.Controls" Namespace="WYJ.Web.Controls" TagPrefix="WYJ" %>
然后添加我们的Repeater
<WYJ:DataPagerRepeater ID="rptLeaveword" runat="server" PersistentDataSource="true"> <ItemTemplate> <div class="leavewordentry"> <div class="datebox"> <div class="time"> <%# ((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Posttime.ToString("HH:mm") %></div> <div class="day"> <%# ((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Posttime.ToString("dd") %> </div> <div class="month"> <%# ((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Posttime.ToString("MMM", new CultureInfo("en-US")).ToUpper() %><%# ((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Posttime.ToString(" yyyy") %></div> </div> <div class="contentbox"> <h2 class="username"> <a id="<%# GeekStudio.Common.IdEncryptor.EncodeId(((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Id) %>" name="<%# GeekStudio.Common.IdEncryptor.EncodeId(((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Id) %>"> <%# ((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Username %></a></h2> <div class="lvwordcontent"> <%# ((GeekStudio.ORM.Model.Leaveword)Container.DataItem).Content %> </div> </div> </div> </ItemTemplate> </WYJ:DataPagerRepeater>
之后添加.NET自带的DataPager,并自定义一些分页样式
<div class="pager"> <div class="fr"> 共<%=Math.Ceiling((double)DataPager1.TotalRowCount / DataPager1.PageSize)%>页,<%=DataPager1.TotalRowCount%>条记录,每页显示 <asp:LinkButton ID="lnkbtn10" CssClass="currentpagesize" runat="server" OnClick="lnkbtn10_Click">10</asp:LinkButton> <asp:LinkButton ID="lnkbtn20" runat="server" OnClick="lnkbtn20_Click">20</asp:LinkButton> <asp:LinkButton ID="lnkbtn30" runat="server" OnClick="lnkbtn30_Click">30</asp:LinkButton> </div> <asp:DataPager ID="DataPager1" PagedControlID="rptLeaveword" runat="server"> <Fields> <asp:NextPreviousPagerField ShowFirstPageButton="True" ShowNextPageButton="False" ShowPreviousPageButton="False" FirstPageText="首页" /> <asp:NextPreviousPagerField ShowNextPageButton="False" ButtonType="Image" PreviousPageImageUrl="~/Images/icons/pagerprevious.png" /> <asp:NumericPagerField CurrentPageLabelCssClass="current" /> <asp:NextPreviousPagerField ShowPreviousPageButton="False" ButtonType="Image" NextPageImageUrl="~/Images/icons/pagernext.png" /> <asp:NextPreviousPagerField ShowLastPageButton="True" ShowNextPageButton="False" ShowPreviousPageButton="False" LastPageText="尾页" /> </Fields> </asp:DataPager> </div>
后台代码:
分页部分不需要代码。下面发的代码是切换每页显示数量的:
protected void lnkbtn10_Click(object sender, EventArgs e) { DataPager1.PageSize = 10; lnkbtn10.CssClass = "currentpagesize"; lnkbtn20.CssClass = ""; lnkbtn30.CssClass = ""; } protected void lnkbtn20_Click(object sender, EventArgs e) { DataPager1.PageSize = 20; lnkbtn20.CssClass = "currentpagesize"; lnkbtn10.CssClass = ""; lnkbtn30.CssClass = ""; } protected void lnkbtn30_Click(object sender, EventArgs e) { DataPager1.PageSize = 30; lnkbtn30.CssClass = "currentpagesize"; lnkbtn10.CssClass = ""; lnkbtn20.CssClass = ""; }
二、自定义GridView
using System; using System.Collections; using System.Web.UI.WebControls; namespace WYJ.Web.Controls { /// <summary> /// DataPagerGridView is a custom control that implements GrieView and IPageableItemContainer /// </summary> public class DataPagerGridView : GridView, IPageableItemContainer { public DataPagerGridView() : base() { PagerSettings.Visible = false; } /// <summary> /// TotalRowCountAvailable event key /// </summary> private static readonly object EventTotalRowCountAvailable = new object(); /// <summary> /// Call base control's CreateChildControls method and determine the number of rows in the source /// then fire off the event with the derived data and then we return the original result. /// </summary> /// <param name="dataSource"></param> /// <param name="dataBinding"></param> /// <returns></returns> protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding) { int rows = base.CreateChildControls(dataSource, dataBinding); // if the paging feature is enabled, determine the total number of rows in the datasource if (this.AllowPaging) { // if we are databinding, use the number of rows that were created, otherwise cast the datasource to an Collection and use that as the count int totalRowCount = dataBinding ? rows : ((ICollection)dataSource).Count; // raise the row count available event IPageableItemContainer pageableItemContainer = this as IPageableItemContainer; this.OnTotalRowCountAvailable(new PageEventArgs(pageableItemContainer.StartRowIndex, pageableItemContainer.MaximumRows, totalRowCount)); // make sure the top and bottom pager rows are not visible if (this.TopPagerRow != null) this.TopPagerRow.Visible = false; if (this.BottomPagerRow != null) this.BottomPagerRow.Visible = false; } return rows; } /// <summary> /// Set the control with appropriate parameters and bind to right chunk of data. /// </summary> /// <param name="startRowIndex"></param> /// <param name="maximumRows"></param> /// <param name="databind"></param> void IPageableItemContainer.SetPageProperties(int startRowIndex, int maximumRows, bool databind) { int newPageIndex = (startRowIndex / maximumRows); this.PageSize = maximumRows; if (this.PageIndex != newPageIndex) { bool isCanceled = false; if (databind) { // create the event arguments and raise the event GridViewPageEventArgs args = new GridViewPageEventArgs(newPageIndex); this.OnPageIndexChanging(args); isCanceled = args.Cancel; newPageIndex = args.NewPageIndex; } // if the event wasn't cancelled change the paging values if (!isCanceled) { this.PageIndex = newPageIndex; if (databind) this.OnPageIndexChanged(EventArgs.Empty); } if (databind) this.RequiresDataBinding = true; } } /// <summary> /// IPageableItemContainer's StartRowIndex = PageSize * PageIndex properties /// </summary> int IPageableItemContainer.StartRowIndex { get { return this.PageSize * this.PageIndex; } } /// <summary> /// IPageableItemContainer's MaximumRows = PageSize property /// </summary> int IPageableItemContainer.MaximumRows { get { return this.PageSize; } } /// <summary> /// /// </summary> event EventHandler<PageEventArgs> IPageableItemContainer.TotalRowCountAvailable { add { base.Events.AddHandler(DataPagerGridView.EventTotalRowCountAvailable, value); } remove { base.Events.RemoveHandler(DataPagerGridView.EventTotalRowCountAvailable, value); } } /// <summary> /// /// </summary> /// <param name="e"></param> protected virtual void OnTotalRowCountAvailable(PageEventArgs e) { EventHandler<PageEventArgs> handler = (EventHandler<PageEventArgs>)base.Events[DataPagerGridView.EventTotalRowCountAvailable]; if (handler != null) { handler(this, e); } } } }
用法与Repeater类似,不多发了~