代码改变世界

【原创】ASP.NET通用分页

2012-06-29 15:34  杨新华  阅读(2143)  评论(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,本文结束:

 转载的请注原创地址,谢谢。