zoukankan      html  css  js  c++  java
  • 用Gridview和ObjectDataSource轻松实现自定义分页

     一.什么是自定义分页

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

    二.为什么使用ObjectDataSource

            ASP.NET2.0提供了SqlDataSource数据源控件提供了ConnectionStringSelectCommandSelectCommandTypeSelectParameters等属性,分别用于指定连接字符串、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表为辅,显示OrderDate1997年之前的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 (11NOT 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

  • 相关阅读:
    Django学习案例一(blog):四. 使用Admin
    Django学习案例一(blog):三. 模型生成数据
    Django学习案例一(blog):二. 连接数据库
    连接Xively云
    undefined reference to `_sbrk', `_write', `_lseek', `_read'
    Android manifest
    关于android socket出现at java.net.DatagramSocket java.net.BindException at libcore.io.IoBridge.bind(IoBridge.java:89)等waring
    virtual box Failed to load unit ""pgm" 的error
    Lwip lwip_recvfrom函数一个数据包不能分多次读取。
    获取Window下的文件缩略图
  • 原文地址:https://www.cnblogs.com/xiaofengfeng/p/2206699.html
Copyright © 2011-2022 走看看