一.什么是自定义分页

自定义分页是与默认分页相对应的。默认分页指一次检索出所有数据并将其绑定到数据绑定控件中,虽然该控件只能一页一页显示这些数据,但是所有数据其实都已经被绑定到控件上了。自定义分页的含义是显示到哪一页就检索并绑定哪一页的数据。显然在大数据量的情况下,自定义分页的效率会高很多。  
        在Asp.net 1.x中自定义分页又称数据库分页,DataGrid中的AllowCustomerPaging属性和VirtualItemCount属性就是专门为自定义分页准备的。在Asp.net 2.0中因为引入了数据源的概念,因此自定义分页也可以叫做数据源分页。

二.为什么使用ObjectDataSource

ASP.NET2.0提供了SqlDataSource数据源控件,提供了ConnectionString、SelectCommand、SelectCommandType、SelectParameters等属性,分别用于指定连接字符串、SQL查询语句、SQL查询语句的类型和查询所使用的参数。SqlDataSource数据源控件根据这些属性的设定从关系数据库中获取数据。但是,SqlDataSource 控件存在一个问题:该控件的缺点在于它迫使您将用户界面层与数据访问层混合在一起,忽略了业务逻辑层。然而随着应用程序规模的扩大,具有清晰的用户界面层、业务逻辑层、数据访问层以及数据实体层是极为必要的。仅仅通过 SqlDataSource 控件的属性,在用户界面层引用 SQL 语句或存储过程是不可取的,或说是缺乏架构意识的,不利于代码的重用和维护。另外,SqlDataSource也不支持数据源分页,也就不能实现自定义分页。
        ObjectDataSource 控件对象模型类似于 SqlDataSource 控件。但ObjectDataSource 提供一个 TypeName 属性(而不是 ConnectionString属性),该属性指定用于执行数据操作的业务逻辑类的类名,ObjectDataSource可以通过TypeName 属性直接调用业务层的类。类似于 SqlDataSource 的命令属性SelectCommand、InsertCommand、UpdateCommand、DeleteCommand,ObjectDataSource 控件支持诸如 SelectMethod、UpdateMethod、InsertMethod 和 DeleteMethod属性,用于指定执行这些操作的方法名。显然ObjectDataSource是依托于一个业务逻辑类的,这样我们就可以拥有完善的架构,业务逻辑类可以为复杂的业务逻辑提供好的支持,也有利于代码的重用和维护。特别是ObjectDataSource 控件提供了EnablePaging属性、SelectCountMethod属性、StartRowIndexParameterName属性和MaximumRowsParameterName属性专门支持数据源分页。 SelectCountMethod属性指定的是获取数据项总数的方法。StartRowIndexParameterName属性用于指定一个参数的名称,如程序中不特别设定,其默认参数名为startRowIndex,该参数代表该页数据项的开始行索引;MaximumRowsParameterName属性也用于指定一个参数名称,其默认参数名为maximumRows,该参数代表一页中容纳的数据项总数。

三.示例

本例是以SQL Server自带的Northwind数据库的Orders表为主,Employees表和Customers表为辅,显示OrderDate在1997年之前的Order列表。

(1). 实体层
        在实体层中创建Order、Employee、Customer三个类,其中Order引用了Employee类和Customer类。

(2).  数据访问层
        数据访问层使用了微软提供的SqlHelper类。

public class OrderDataAccess
    {
        private string  ConnectionString = Convert.ToString(ConfigurationManager.ConnectionStrings["NorthWindConnectionString"]);

public OrderDataAccess()
        {
            //
            // TODO: Add constructor logic here
            //
        }

public int CountTotalNumber()
        {
            return Convert.ToInt32(SqlHelper.ExecuteScalar(ConnectionString, CommandType.StoredProcedure, "Order_Select_TotalNumber")); 
        }

public IEnumerable FindOrders(int startRowIndex, int maximumRows,bool isDataSet)
        {
            SqlParameter[] parms = {
                new SqlParameter("@StartRowIndex",SqlDbType.Int,4),
                new SqlParameter("@MaximumRows",SqlDbType.Int,4)
            };
            parms[0].Value = startRowIndex;
            parms[1].Value = maximumRows;

if(!isDataSet)
            {
                ArrayList OrderList = new ArrayList();
                using (SqlDataReader reader = SqlHelper.ExecuteReader(ConnectionString, CommandType.StoredProcedure, "Order_Select_Pagination", parms))
                {
                    while (reader.Read())
                    {
                        OrderList.Add(LoadOrder(reader));
                    }
                }
                return OrderList;
            }
            else
            {
                DataSet ds = SqlHelper.ExecuteDataset(ConnectionString, CommandType.StoredProcedure, "Order_Select_Pagination", parms);
                return ds.Tables[0].DefaultView;
            }
        }

public int DeleteOrder(int orderId)
        {
            SqlParameter parm = new SqlParameter("@OrderID",SqlDbType.Int,4);
            parm.Value = orderId;

return SqlHelper.ExecuteNonQuery(ConnectionString,CommandType.StoredProcedure,"Order_Delete",parm);
        }

private Order LoadOrder(SqlDataReader reader)
        {
            Order order = new Order();

order.OrderId = Convert.ToInt32(reader["OrderID"]);
            order.Customer = new Customer(Convert.ToString(reader["CustomerID"]), Convert.ToString(reader["CompanyName"]));
            order.Employee = new Employee(Convert.ToInt32(reader["EmployeeID"]), Convert.ToString(reader["LastName"]), Convert.ToString(reader["FirstName"]));
            order.OrderDate = Convert.ToDateTime(reader["OrderDate"]);
            order.RequiredDate = Convert.ToDateTime(reader["RequiredDate"]);
            order.ShippedDate = Convert.ToDateTime(reader["ShippedDate"]);
            order.ShipVia = Convert.ToInt32(reader["ShipVia"]);
            order.Freight = Convert.ToDecimal(reader["Freight"]);
            order.ShipName = Convert.ToString(reader["ShipName"]);
            order.ShipAddress = Convert.ToString(reader["ShipAddress"]);
            order.ShipCity = Convert.ToString(reader["ShipCity"]);
            order.ShipRegion = Convert.ToString(reader["ShipRegion"]);
            order.ShipPostalCode = Convert.ToString(reader["ShipPostalCode"]);
            order.ShipCountry = Convert.ToString(reader["ShipCountry"]);

return order;
        }
    }

注意FindOrders方法,它带有三个参数,startRowIndex代表起始行的索引,maxmiumRows代表本次查询所要获得的数据项总数,isDataSet是为标示是使用DataSet还是SqlDataReader,如果表示层的GridView设置了属性AllowSorting为true,也就是要求具有排序功能,那么数据访问层就必须使用DataSet来容纳数据,否则使用SqlDataReader就可以了。

(3). 存储过程

获取符合要求的总订单数存储过程:

ALTER PROCEDURE Order_Select_TotalNumber

AS

SET NOCOUNT ON

Select Count(OrderID)

From Orders

Where OrderDate < '1997'

RETURN 

分页获取数据的存储过程:

ALTER PROCEDURE Order_Select_Pagination 
(
    @StartRowIndex int = null
,
    @MaximumRows int = null

)
AS
SET NOCOUNT ON
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int

-- Set the page bounds
SET @PageLowerBound = @StartRowIndex
SET @PageUpperBound = @PageLowerBound + @MaximumRows + 1

-- Create a temp table to store the select results
CREATE TABLE #tmp
(
     RecNo int IDENTITY (1, 1) NOT NULL
,
     OrderID int

)

INSERT INTO #tmp
        SELECT [OrderID]

        FROM [Orders]
        Where OrderDate < '1997'
        ORDER BY OrderID ASC

SELECT o.*,e.LastName,e.FirstName,c.CompanyName
FROM Orders o inner join Employees e on o.EmployeeID = e.EmployeeID inner join Customers c on o.CustomerID =
 c.CustomerID, #tmp t
WHERE o.OrderID = t.OrderID AND

     t.RecNo > @PageLowerBound AND
     t.RecNo < @PageUpperBound
ORDER BY t.RecNo

RETURN

(4). 业务逻辑层
        本例的业务逻辑很简单,只是作为表示层和数据访问层之间的桥梁,并没有掺杂其它的运算逻辑。
业务逻辑类中的方法可以设置给ObjectDataSource控件的SelectCountMethod属性和SelectMethod属性,这样ObjectDataSource就可以自动通过业务逻辑类获得数据了。

public class OrderBusinessLogic
    {
        private static OrderDataAccess orderAccess = new OrderDataAccess();
        
        public OrderBusinessLogic()
        {
            //
            // TODO: Add constructor logic here
            //
        }

public static int GetRowsTotalNumber()
        {
            return orderAccess.CountTotalNumber();
        }

public static IEnumerable GetOrdersForPagingAndSorting(int startRowIndex, int maximumRows)
        {
           return orderAccess.FindOrders(startRowIndex, maximumRows,true);          
        }

public static IEnumerable GetOrdersForPaging(int startRowIndex, int maximumRows)
        {
            return orderAccess.FindOrders(startRowIndex, maximumRows, false);
        }

public static void DeleteOrder(int orderId)
        {
            orderAccess.DeleteOrder(orderId);
        }       
    }

值得注意的两点是:第一,这里的方法都是用了静态方法,其实也可以不使用静态方法。不使用静态方法时Asp.net会先实例化ObjectDataSource的TypeName中设定的类,然后调用它的方法。第二,GetOrdersForPagingAndSorting和GetOrdersForPaging两个方法,前者是为了应对GridView的排序要求,因为为了能够实现排序,必须使用DataView、DataTable或DataSet;而后者则不用于排序,只需返回一个SqlDataReader。

(5).页面程序
        如果想只使用下图所示GridView的默认分页样式,则按照下面的页面代码,不必再写任何后台代码就可实现。

 <asp:GridView ID="OrdersGridView" DataSourceID="OrdersObjectDataSource" AutoGenerateColumns="false"
            AllowPaging="true" runat="server" AllowSorting="true" Width="720px" PageSize="20">
            <PagerStyle ForeColor="Blue" BackColor="LightBlue" />            
            <Columns>
                <asp:BoundField HeaderText="Order Id" DataField="OrderId"  />
                <asp:TemplateField HeaderText="Customer">
                    <ItemTemplate>
                        <%# Eval("Customer.CompanyName")%>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Employee">
                    <ItemTemplate>
                        <%# Eval("Employee.EmployeeName")%>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:BoundField HeaderText="Order date" DataField="OrderDate" DataFormatString="{0:g}" />
                <asp:BoundField HeaderText="Required date" DataField="RequiredDate" DataFormatString="{0:d}" />
                <asp:BoundField HeaderText="Shipped date" DataField="ShippedDate" DataFormatString="{0:d}" />
                <asp:BoundField HeaderText="Ship address" DataField="ShipAddress" />
                <asp:BoundField HeaderText="Ship country" DataField="ShipCountry" />                
            </Columns>
        </asp:GridView>
        <asp:ObjectDataSource ID="OrdersObjectDataSource" runat="server" SelectCountMethod="GetRowsTotalNumber"
          SelectMethod="GetOrdersForPaging" TypeName="MyTest.BusinessLogic.OrderBusinessLogic" OldValuesParameterFormatString="Original_{0}" EnablePaging="true">          
        </asp:ObjectDataSource>

如果想实现如下图所示的自定义的分页样式,则参考下列代码:

<asp:GridView ID="OrdersGridView" DataSourceID="OrdersObjectDataSource" AutoGenerateColumns="false"
            AllowPaging="true" AllowSorting="true" OnDataBound="OrdersGridView_DataBound" runat="server" 
            Width="720px" PageSize="25" OnRowDeleted="OrdersGridView_RowDeleted" DataKeyNames="OrderID">
            <Columns>
                <asp:BoundField HeaderText="Order Id" DataField="OrderID" SortExpression="OrderID"/>
                <asp:BoundField HeaderText="Customer company" DataField="CompanyName" SortExpression="CompanyName"/>
                <asp:TemplateField HeaderText="Employee" SortExpression="EmployeeName">
                  <ItemTemplate>
                      <%# Eval("LastName")+" "+Eval("FirstName") %>  
                  </ItemTemplate>
                </asp:TemplateField>                                                
                <asp:BoundField HeaderText="Order date" DataField="OrderDate" DataFormatString="{0:g}" SortExpression="OrderDate"/>
                <asp:BoundField HeaderText="Required date" DataField="RequiredDate" DataFormatString="{0:d}" SortExpression="RequiredDate"/>
                <asp:BoundField HeaderText="Shipped date" DataField="ShippedDate" DataFormatString="{0:d}" SortExpression="ShippedDate"/>
                <asp:BoundField HeaderText="Ship address" DataField="ShipAddress" />
                <asp:BoundField HeaderText="Ship country" DataField="ShipCountry" />                
                <asp:CommandField ButtonType="Button" DeleteText="删除" ShowDeleteButton="true" HeaderText="Operation"  />
            </Columns>
            <PagerStyle ForeColor="Blue" BackColor="LightBlue" />            
            <PagerTemplate>
                <table width="100%">
                    <tr>
                        <td width="70%">
                            <asp:Label ID="MessageLabel" ForeColor="Blue" Text="页码:" runat="server" />
                            <asp:DropDownList ID="PageDropDownList" AutoPostBack="true" OnSelectedIndexChanged="PageDropDownList_SelectedIndexChanged"
                                runat="server" />
                            <asp:LinkButton CommandName="Page" CommandArgument="First" ID="linkBtnFirst" runat="server">首页</asp:LinkButton>
                            <asp:LinkButton CommandName="Page" CommandArgument="Prev" ID="linkBtnPrev" runat="server">上一页</asp:LinkButton>
                            <asp:LinkButton CommandName="Page" CommandArgument="Next" ID="linkBtnNext" runat="server">下一页</asp:LinkButton>
                            <asp:LinkButton CommandName="Page" CommandArgument="Last" ID="linkBtnLast" runat="server">末页</asp:LinkButton>
                        </td>
                        <td align="right">
                            <asp:Label ID="CurrentPageLabel" ForeColor="Blue" runat="server" />
                        </td>
                    </tr>
                </table>
            </PagerTemplate>
        </asp:GridView>
        <asp:ObjectDataSource ID="OrdersObjectDataSource" runat="server" SelectCountMethod="GetRowsTotalNumber"
          SelectMethod="GetOrdersForPagingAndSorting" DeleteMethod="DeleteOrder"
          TypeName="MyTest.BusinessLogic.OrderBusinessLogic" EnablePaging="true" EnableViewState="true">          
        <DeleteParameters>
            <asp:Parameter Name="OrderId" Type="Int32" Direction="Input" />
        </DeleteParameters>
        </asp:ObjectDataSource>

注意页导航模板PagerTemplate属性的使用。通常将按钮控件(如上面代码中的LinkButton)添加到页导航模板以执行分页操作。单击 CommandName 属性设置为“Page”的按钮控件时,GridView 控件会执行分页操作。按钮的 CommandArgument 属性确定要执行的分页操作的类型。下表列出了 GridView 控件支持的命令参数值。 

CommandArgument 值

说明

“Next”

导航至下一页。

“Prev”

导航至上一页。

“First”

导航至第一页。

“Last”

导航至最后一页。

整数值

导航至指定页码。

页面程序的后台代码:

public partial class TestGridview2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

}

protected void PageDropDownList_SelectedIndexChanged(Object sender, EventArgs e)
    {
        GridViewRow pagerRow = OrdersGridView.BottomPagerRow;
        DropDownList pageList = (DropDownList)pagerRow.Cells[0].FindControl("PageDropDownList");
        OrdersGridView.PageIndex = pageList.SelectedIndex;
    }

protected void OrdersGridView_DataBound(Object sender, EventArgs e)
    {
        GridViewRow pagerRow = OrdersGridView.BottomPagerRow;
        LinkButton linkBtnFirst = (LinkButton)pagerRow.Cells[0].FindControl("linkBtnFirst");
        LinkButton linkBtnPrev = (LinkButton)pagerRow.Cells[0].FindControl("linkBtnPrev");
        LinkButton linkBtnNext = (LinkButton)pagerRow.Cells[0].FindControl("linkBtnNext");
        LinkButton linkBtnLast = (LinkButton)pagerRow.Cells[0].FindControl("linkBtnLast");
        if (OrdersGridView.PageIndex == 0)
        {
            linkBtnFirst.Enabled = false;
            linkBtnPrev.Enabled = false;
        }
        else if (OrdersGridView.PageIndex == OrdersGridView.PageCount - 1)
        {
            linkBtnLast.Enabled = false;
            linkBtnNext.Enabled = false;
        }
        else if (OrdersGridView.PageCount <= 0)
        {
            linkBtnFirst.Enabled = false;
            linkBtnPrev.Enabled = false;
            linkBtnNext.Enabled = false;
            linkBtnLast.Enabled = false;
        }
        DropDownList pageList = (DropDownList)pagerRow.Cells[0].FindControl("PageDropDownList");
        Label pageLabel = (Label)pagerRow.Cells[0].FindControl("CurrentPageLabel");
        if (pageList != null)
        {
            for (int i = 0; i < OrdersGridView.PageCount; i++)
            {
                int pageNumber = i + 1;
                ListItem item = new ListItem(pageNumber.ToString() + "/" + OrdersGridView.PageCount.ToString(), pageNumber.ToString());
                if (i == OrdersGridView.PageIndex)
                {
                    item.Selected = true;
                }
                pageList.Items.Add(item);
            }
        }
        if (pageLabel != null)
        {
            int currentPage = OrdersGridView.PageIndex + 1;
            pageLabel.Text = "当前页: " + currentPage.ToString() +
              " / " + OrdersGridView.PageCount.ToString();
        }
    }

protected void OrdersGridView_RowDeleted(object sender,GridViewDeletedEventArgs e)
    {
        if (e.Exception == null && OrdersGridView.Rows.Count == 1)
        {
            // we just deleted the last row
            OrdersGridView.PageIndex = Math.Max(0, OrdersGridView.PageIndex - 1);
         }
    }
}

由以上示例可以看出GridView关联ObjectDataSource时,省去了DataBind方法的使用。也就是说只要给GridView关联上数据源控件,那么绑定的事程序员就不用操心了。当GridView发生翻页事件时整个的运行过程是这样的,GridView的PageIndex变成新值,ObjectDataSource根据GridView的PageIndex属性换算出startRowIndex和MaxmiumRows的值,然后传递这两个参数给SelectMethod指定的方法从而获得数据,然后再调用SelectCountMethod指定的方法获得总数据项数,以计算出总页数,然后执行OrderGridView_DataBound事件处理方法最终完成绑定工作。
    注意OrdersGridView_RowDeleted事件处理方法的写法,它是为了应对将最后一页的最后一条数据删除之后,GridView将能够识别出最后一页已经被删空了,因此原来的倒数第二页就变成了现在的末页了,并让GridView的当前页指向末页。
    下载完整源程序/Files/taewind/TestDataBindControlls.rar

用Gridview和ObjectDataSource轻松实现自定义分页相关推荐

  1. WinForm轻松实现自定义分页 (转载)

    转载至http://xuzhihong1987.blog.163.com/blog/static/267315872011315114240140/ 以前都是做web开发,最近接触了下WinForm, ...

  2. ASP.NET 2.0在SQL Server 2005上自定义分页

    这篇文章讲述了如何利用SQL Server 2005的新特性来简单高效的实现分页.对于那些暂时还没用到SQL Server2005的人们,请看在大规模数据中的高效分页方法.如果需要,这篇文章会补上这里 ...

  3. 上接扩展GridView控件(10) - 自定义分页样式

    5.重写OnRowCreated以实现自定义分页样式 /// <summary>                  /// OnRowCreated                  // ...

  4. GridView自定义分页导航

    自己做的一个项目中所运用到的技术:| 1.         日历控件(带时分秒) 2.         GridView 批量删除,自定义分页,定位页码 3.         GridView 修改 ...

  5. GridView自定义分页样式(上一页,下一页,到第几页)(新手教程)

    今天要为网站做一个文章列表,发现GridView的分页样式很难看,于是结合网上的例子,自己做了一个.不是很美观,不过还是很实用的,先看下效果吧,如图(1).演示地址http://www.veryam. ...

  6. GridView实战一:自定义分页、排序、修改、插入、删除

    前言: 在某次公司面试时被问到对GridView操作的熟悉程度,在那之前一直用Repeater内嵌table标签对GridView操作确实很少,于是最近在项目的后台上对GridView进行了一番实操, ...

  7. ObjectDataSource自定义分页

    ObjectDataSource是唯一支持自定义分页的数据源,要实现分页效果,首先要将ObjectDataSource.EnablePageing属性设为true,通过三个属性实现:StartRowI ...

  8. ASP.NET技巧:GridView控件自定义分页详解第一页

    ASP.NET技巧:GridView控件自定义分页详解 日期:2007年9月11日 作者: 查看:[大字体 中字体 小字体] <script src="../gg/info468.js ...

  9. ASP.NET 2.0数据教程之二十六::排序自定义分页数据

    导言 和默认翻页方式相比,自定义分页能提高几个数量级的效率.当我们的需要对大量数据分页的时候就需要考虑自定义分页,然而实现自定义分页相比默认分页需要做更多工作.对于排序自定义分页数据也是这样,在本教程 ...

最新文章

  1. Qt中文手册 之 QHeaderView
  2. 破解世界性技术难题! GTS让分布式事务简单高效
  3. unity 中的UGUI 屏蔽鼠标穿透
  4. 面试官让我讲下线程的WAITING状态,我笑了
  5. JS实现逼真的雪花飘落特效
  6. 计算机应用技木就业前京,计算机专业毕业的研究生在京就业情况及启示.doc
  7. 7-10 找最小的字符串 (15 分)
  8. 《移动App测试的22条军规》—App测试综合案例分析23.11节测试微信App对多语言和地区的支持...
  9. 再暴BBSxp 7.0 Beta 2漏洞
  10. 华为虚拟机eNSP命令大全(所有命令)
  11. 直连网线和交叉网线使用区别
  12. M1 和 M2的走势解读
  13. 2、sudo时候出现no valid sudoers sources found, quitting
  14. 最新表情包小程序+前后端去授权版/最火表情包小程序源码
  15. 文化袁探索专栏——Activity、Window和View三者间关系
  16. Openharmony应用NAPI详解--基础篇
  17. Canopen对象字典学习
  18. 03-白龙马与拉磨驴的人生
  19. 读书笔记-《购物中心》
  20. socket 是用什么网络协议

热门文章

  1. laravel静态资源
  2. 淘宝API调用 申请 获取session key
  3. CSS篇 第9章 Visual Formatting Model 部分翻译
  4. Windows 下开发PHP扩展资源
  5. linq Distinct
  6. IOS开发基础之摇奖机案例
  7. python场景异常_python-异常
  8. springaop事务逻辑原理_搞懂Spring AOP,这一篇就够了
  9. arcserver连接oracle,ArcSDE的二种连接方式(应用服务器连接,直接连接)
  10. python head 函数_python爬虫中header是什么?怎么用?