【原创】ASP.NET通用分页
2012-06-29 15:34 杨新华 阅读(2158) 评论(2) 编辑 收藏 举报一说到“分页”,大家可能不会很陌生,如果做过ASP.NET的,基本都做过,接触过分页,更有甚者更是精通。如果精通的分页的人就不用看这文章了。其内容也是平平。只是给刚接触分页和不是很了解分页的查看一下。
通常分页分以下几种方式实现:
(1)一些数据绑定控件自带的分页控件(例如GridView)
(2)应用分页类PagedDataSource
(3)用开源分页AspNetPager。它的功能也比较强大。
(4)手动写前台Html和数据库的分页存储过程
。。。等等。前三种方式不灵活,性能也有相应问题,个人比较倾向于第四种方式,它是自己可以控制的。
我们在写分页时一定要考虑以下几个点。
(1)效率问题与速度问题,数据量比较大时,不能一次性把数据加载到内存,而是需要哪一页的数据时,自动去加载需要的那一页数据。
(2)通用性问题,我们写分页时,不能一直有相同的重复代码。最好封装起来。在任何页面都可以用。
多余的就不说了。写这文章之前,也参考了网上写的比较好的例子。那么我就来总结一下吧。
一种方式
首先:建立一个Web用户控件(为了通用性嘛),代码如下:
1 <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Pager3.ascx.cs" Inherits="PageS.Pager" %> 2 3 4 <asp:LinkButton ID="hylfirst" runat="server" onclick="reBind_Click">首页</asp:LinkButton> 5 <asp:LinkButton ID="hylprev" runat="server" onclick="reBind_Click">上一页</asp:LinkButton> 6 <asp:LinkButton ID="hylnext" runat="server" onclick="reBind_Click">下一页</asp:LinkButton> 7 <asp:LinkButton ID="hylend" runat="server" onclick="reBind_Click">尾页</asp:LinkButton> 8 9 共<asp:Label ID="lbRecord" runat="server" Text="Label"></asp:Label>条记录 10 共<asp:Label ID="lbpage" runat="server" Text="Label"></asp:Label>页 11 当前为第<asp:Label ID="lbRow" runat="server" Text=""></asp:Label>页
后台:
1 //当前页码 2 private int _pageindex; 3 public int PageIndex 4 { 5 get 6 { 7 if (_pageindex == 0) 8 { 9 _pageindex = 1; 10 } 11 return _pageindex; 12 } 13 set { _pageindex = value; } 14 } 15 //一页的大小 16 private int _pageSize; 17 public int PageSize 18 { 19 get 20 { 21 if (_pageSize == 0) 22 { 23 _pageSize = 1; 24 } 25 return _pageSize; 26 } 27 set { _pageSize = value; } 28 } 29 //总记录数 30 private int _recordCount; 31 public int RecordCount 32 { 33 get { return _recordCount; } 34 set { _recordCount = value; } 35 } 36 37 protected void Page_Load(object sender, EventArgs e) 38 { 39 if (!IsPostBack) 40 { 41 LoadDataBind(); 42 } 43 } 44 //绑定 45 public void LoadDataBind() 46 { 47 int pageCount = (RecordCount % PageSize) == 0 ? RecordCount / PageSize : RecordCount / PageSize + 1;//页数 48 49 initialization(pageCount); 50 } 51 //数据初始化 52 private void initialization(int pageCount) 53 { 54 //--------求出总记录数 55 lbRecord.Text = RecordCount.ToString(); 56 //--------当前为第几页 57 lbRow.Text = PageIndex.ToString(); 58 //--------共多少页 59 lbpage.Text = pageCount.ToString(); 60 61 this.hylfirst.Enabled = true; 62 this.hylprev.Enabled = true; 63 this.hylnext.Enabled = true; 64 this.hylend.Enabled = true; 65 66 if (Convert.ToInt32(this.lbRow.Text) == 1) 67 { 68 this.hylfirst.Enabled = false; 69 this.hylprev.Enabled = false; 70 } 71 if (Convert.ToInt32(this.lbRow.Text) == pageCount) 72 { 73 this.hylnext.Enabled = false; 74 this.hylend.Enabled = false; 75 } 76 77 this.hylfirst.CommandArgument = "1"; 78 this.hylprev.CommandArgument = (PageIndex - 1).ToString(); 79 this.hylnext.CommandArgument = (PageIndex + 1).ToString(); 80 this.hylend.CommandArgument = pageCount.ToString(); 81 82 } 83 84 //定义一个委托 85 public delegate void reBindEvent(object sender, EventArgs arg); 86 public event reBindEvent reBind; 87 88 protected void reBind_Click(object sender, EventArgs e) 89 { 90 if (this.reBind != null) 91 this.reBind(sender, e); 92 LoadDataBind(); 93 }
说明:在代码的最后利用了委托和事件。这是为什么呢。因为我们在点击自定义控件的按扭(上一页,下一页,首页,未页.....)时,要调用Aspx页面绑定数据的方法。我们在这里不能直接实例化aspx页面类,再去调用其方法。
我们为了通用必须这样去做。有关ASPX和自定义控件之间的赋值,互相调用,大家可以去网上找此资料来看,本章不讲解这些内容。
好了,我们已经做好通用性了,那么我们如何调用呢。
建立一个测试页面,代码如下:
1 <%@ Register src="Pager.ascx" tagname="Pager" tagprefix="uc1" %> 2 3 4 5 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 6 7 <html xmlns="http://www.w3.org/1999/xhtml" > 8 <head runat="server"> 9 <title>利用反射机制</title> 10 </head> 11 <body> 12 <form id="form1" runat="server"> 13 <div> 14 <asp:GridView ID="gvUsers" runat="server" ></asp:GridView> 15 <uc1:Pager ID="Pager1" runat="server" OnreBind="LoadData" /> 16 17 </div> 18 19 20 </form> 21 </body> 22 </html>
后台:
1 protected void Page_Load(object sender, EventArgs e) 2 { 3 if (!IsPostBack) 4 { 5 LoadData(null,null); 6 } 7 } 8 protected void LoadData(object sender, EventArgs e) 9 { 10 if (sender != null) 11 { 12 Pager1.PageIndex = Convert.ToInt32(((LinkButton)sender).CommandArgument); 13 } 14 else 15 { 16 Pager1.PageIndex = 1; 17 } 18 int recordcount = 0; 19 Pager1.PageSize = 20; 20 21 DataTable tables=new DataTable(); 22 23 //通过当前页码,页大小,去数据库调用本页的数据,并且返回总记录数 24 //tables= BLL.GetCurrentPageData(Pager1.PageIndex, Pager1.PageSize,out count); 25 26 gvUsers.DataSource = tables; 27 gvUsers.DataBind(); 28 Pager1.RecordCount = recordcount; 29 30 31 }
说明: <uc1:Pager ID="Pager1" runat="server" OnreBind="LoadData" /> 看到OnreBind="LoadData"了吗, 这个是我们在自定义控件中定义的事件。在这个订阅一下。
以便在自定义控件中点击按扭时,自动来触发这个事件。从而调用LoadData这个方法。
说了半天,好像还没出给出数据库分页的通用存储过程来。
好了,那我也直接粘出来供大家来分享吧。单表的通用分页存储过程
CREATE PROCEDURE [dbo].[_tgy_BaseGetPageList] ( @tblName varchar(255), -- 表名 @strGetFields varchar(1000) = '*', -- 需要返回的列 @fldName varchar(255) = '', -- 排序的字段名 @PageSize int = 10, -- 页尺寸 @PageIndex int = 1, -- 页码 @doCount bit = 0, -- 返回记录总数, 非 0 值则返回 @OrderType bit = 0, -- 设置排序类型, 非 0 值则降序 @strWhere varchar(1500) = '' -- 查询条件 (注意: 不要加 where) ) AS declare @strSQL varchar(5000) -- 主语句 declare @strTmp varchar(110) -- 临时变量 declare @strOrder varchar(400) -- 排序类型 if @doCount != 0 begin if @strWhere !='' set @strSQL = 'select count(*) as Total from ' + @tblName + ' where ' + @strWhere else set @strSQL = 'select count(*) as Total from ' + @tblName + '' end --以上代码的意思是如果@doCount传递过来的不是0,就执行总数统计。以下的所有代码都是@doCount为0的情况: else begin if @OrderType != 0 begin set @strOrder = ' order by ' + @fldName + ' desc' --如果@OrderType不是0,就执行降序,这句很重要! end else begin set @strOrder = ' order by ' + @fldName end if (@PageIndex = 1 or @PageIndex = 0) begin if @strWhere != '' set @strSQL = 'select top ' + str(@PageSize) + ' ' + @strGetFields + 'from ' + @tblName + ' where ' + @strWhere + ' ' + @strOrder else set @strSQL = 'select top ' + str(@PageSize) + ' ' + @strGetFields + 'from ' + @tblName + ' ' + @strOrder --如果是第一页就执行以上代码,这样会加快执行速度 end else begin --以下代码赋予了@strSQL以真正执行的SQL代码 set @strSQL = 'select top ' + str(@PageSize) + ' ' + @strGetFields + ' from ' + ' ( select '+ @strGetFields + ' ,Row_Number() OVER('+@strOrder+') as row' + ' from ' + @tblName + ' ) aaa' + ' where row > ' + str((@PageIndex-1)*@PageSize) if @strWhere != '' set @strSQL = 'select top ' + str(@PageSize) + ' ' + @strGetFields + ' from ' + ' ( select '+ @strGetFields + ' ,Row_Number() OVER('+@strOrder+') as row' + ' from ' + @tblName + ' where ' + @strWhere + ' ) aaa' + ' where row > ' + str((@PageIndex-1)*@PageSize) end end exec (@strSQL)
有的人一看,怎么就针对一张表呢,我在写存储过程的时候很多时候会涉及多张表,这个也不通用呀。好了,毕竟网上有能人。我直接粘下代码。多表通用分页存储过程
1 CREATE PROCEDURE [dbo].[Proc_SplitPage] 2 ( 3 @tblName nvarchar(200), ----要显示的表或多个表的连接 4 @fldName nvarchar(1000) = '*', ----要显示的字段列表 5 @pageSize int = 10, ----每页显示的记录个数 6 @page int = 1, ----要显示那一页的记录 7 @fldSort nvarchar(200), ----排序字段列表或条件 8 @Sort bit = 0, ----排序方法,0为升序,1为降序(如果是多字段排列Sort指代最后一个排序字段的排列顺序(最后一个排序字段不加排序标记)--程序传参如:' SortA Asc,SortB Desc,SortC ') 9 @strCondition nvarchar(2000) = null, ----查询条件,不需where 10 @ID nvarchar(150), ----主表的主键 11 @Dist bit = 0, ----是否添加查询字段的 DISTINCT 默认0不添加/1添加 12 @pageCount int = 1 output, ----查询结果分页后的总页数 13 @Counts int = 1 output ----查询到的记录数 14 ) 15 AS 16 SET NOCOUNT ON 17 Declare @sqlTmp nvarchar(1000) ----存放动态生成的SQL语句 18 Declare @strTmp nvarchar(4000) ----存放取得查询结果总数的查询语句 19 Declare @strID nvarchar(1000) ----存放取得查询开头或结尾ID的查询语句 20 21 Declare @strSortType nvarchar(10) ----数据排序规则A 22 Declare @strFSortType nvarchar(10) ----数据排序规则B 23 24 Declare @SqlSelect nvarchar(50) ----对含有DISTINCT的查询进行SQL构造 25 Declare @SqlCounts nvarchar(50) ----对含有DISTINCT的总数查询进行SQL构造 26 27 28 if @Dist = 0 29 begin 30 set @SqlSelect = 'select ' 31 set @SqlCounts = 'Count(*)' 32 end 33 else 34 begin 35 set @SqlSelect = 'select distinct ' 36 set @SqlCounts = 'Count(DISTINCT '+@ID+')' 37 end 38 39 40 if @Sort=0 41 begin 42 set @strFSortType=' ASC ' 43 set @strSortType=' DESC ' 44 end 45 else 46 begin 47 set @strFSortType=' DESC ' 48 set @strSortType=' ASC ' 49 end 50 51 --------生成查询语句-------- 52 --此处@strTmp为取得查询结果数量的语句 53 if @strCondition is null or @strCondition='' --没有设置显示条件 54 begin 55 set @sqlTmp = @fldName + ' From ' + @tblName 56 set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName 57 set @strID = ' From ' + @tblName 58 end 59 else 60 begin 61 set @sqlTmp = + @fldName + 'From ' + @tblName + ' where (1>0) ' + @strCondition 62 set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName + ' where (1>0) ' + @strCondition 63 set @strID = ' From ' + @tblName + ' where (1>0) ' + @strCondition 64 end 65 66 67 68 ----取得查询结果总数量----- 69 exec sp_executesql @strTmp,N'@Counts int out ',@Counts out 70 declare @tmpCounts int 71 if @Counts = 0 72 set @tmpCounts = 1 73 else 74 set @tmpCounts = @Counts 75 76 --取得分页总数 77 set @pageCount=(@tmpCounts+@pageSize-1)/@pageSize 78 79 /**//**当前页大于总页数 取最后一页**/ 80 if @page>@pageCount 81 set @page=@pageCount 82 83 --/*-----数据分页2分处理-------*/ 84 declare @pageIndex int --总数/页大小 85 declare @lastcount int --总数%页大小 86 87 set @pageIndex = @tmpCounts/@pageSize 88 set @lastcount = @tmpCounts%@pageSize 89 if @lastcount > 0 90 set @pageIndex = @pageIndex + 1 91 else 92 set @lastcount = @pagesize 93 94 --//***显示分页 95 if @strCondition is null or @strCondition='' --没有设置显示条件 96 begin 97 if @pageIndex<2 or @page<=@pageIndex / 2 + @pageIndex % 2 --前半部分数据处理 98 begin 99 set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName 100 +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName 101 +' order by '+ @fldSort +' '+ @strFSortType+')' 102 +' order by '+ @fldSort +' '+ @strFSortType 103 end 104 else 105 begin 106 set @page = @pageIndex-@page+1 --后半部分数据处理 107 if @page <= 1 --最后一页数据显示 108 set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@lastcount as VARCHAR(4))+' '+ @fldName+' from '+@tblName 109 +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 110 else 111 set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName 112 +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName 113 +' order by '+ @fldSort +' '+ @strSortType+')' 114 115 +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 116 end 117 end 118 119 else --有查询条件 120 begin 121 if @pageIndex<2 or @page<=@pageIndex / 2 + @pageIndex % 2 --前半部分数据处理 122 begin 123 set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName +' from '+@tblName 124 +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName 125 +' Where (1>0) ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType+')' 126 +' ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType 127 end 128 else 129 begin 130 set @page = @pageIndex-@page+1 --后半部分数据处理 131 if @page <= 1 --最后一页数据显示 132 set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@lastcount as VARCHAR(4))+' '+ @fldName+' from '+@tblName 133 +' where (1>0) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 134 else 135 set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName 136 +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName 137 +' where (1>0) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+')' 138 + @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 139 end 140 end 141 142 ------返回查询结果----- 143 exec sp_executesql @strTmp 144 --print @strTmp 145 SET NOCOUNT OFF 146
本文就更新到这里,后台也会有我其它的分页实现,以上代码中涉及自定义控件调用aspx类的方法,其实也可以用反射机制来实现,大家试试吧。以后我也会涉及。可能也有人对怎么使用通用存储过程也有疑问,我也会在接下来的几篇文章中细讲。
Ok,本文结束:
转载的请注原创地址,谢谢。