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

  • 相关阅读:
    Atitit.随时间变色特效 ---包厢管理系统的规划
    Atitit.request http乱码的设计防止 检测与解决最近实践p825 attilax总结.doc
    Atitit.request http乱码的设计防止 检测与解决最近实践p825 attilax总结.doc
    atitit.薄伽梵歌overview  attilax 读后感
    Atitit。 《吠陀》 《梨俱吠陀》overview 经读后感  是印度上古时期一些文献的总称
    Atitit。 《吠陀》 《梨俱吠陀》overview 经读后感  是印度上古时期一些文献的总称
    atitit.薄伽梵歌overview  attilax 读后感
    Atitit 《摩奴法典》overivew 读后感 不是由国王 颁布的,而是 僧侣编制
    Atitit 《摩奴法典》overivew 读后感 不是由国王 颁布的,而是 僧侣编制
    Atitit.执行cli cmd的原理与调试
  • 原文地址:https://www.cnblogs.com/yangjunwl/p/980237.html
Copyright © 2011-2022 走看看