65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 中的自定义数据分页

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.70/5 (10投票s)

2003年5月30日

4分钟阅读

viewsIcon

159474

downloadIcon

2356

如何使用 Repeater 控件和一些 T-SQL 编程来实现数据分页。

Sample Image - CustomPaging.gif

引言

尽管 DataGrid 控件通过启用 AllowPaging 属性为 DataSource 的记录提供了内置的分页支持,但微软发现,以这种方式分页存在一个很大的缺点:当数据库中有成千上万条记录或更多时,每次导航到新页面时,这些记录都必须从数据源检索到内存中。这会严重降低性能。因此,微软允许我们通过启用 AllowCustomPaging 来实现自定义分页解决方案,以绕过此限制。现在,您只需检索当前页面所需的记录,而不是检索所有记录来显示每一页。

这听起来像是我们需要的,但我发现以这种方式自定义数据分页也有其缺点,因为它只适用于具有标识列且标识列没有缺失值的表。如果某些值缺失,DataGrid 将会显示某些页面的记录少于其他页面。例如,DataGrid 准备显示 10 条唯一 ID 从 10 到 20 的记录,但由于某些原因,最后 5 条记录丢失了。您会期望 DataGrid 显示从 10 到 25 的十条记录,但实际上它不会。它只会显示从 10 到 20 的五条记录,而这并不是您想要的。

因此,为了解决这个问题,我将在这里演示如何通过使用 Repeater 和 T-SQL 编程来实现我们自己的自定义分页解决方案,这需要多做一些工作。我们可以改用 DataListDataGrid,但 Repeater 控件是最轻量级的,而且我们不需要 DataListDataGrid 控件提供的任何特殊功能。

要理解本文,我们只需要创建一个数据库和一个网页。让我们从创建数据库开始。

创建数据库

我们将创建一个名为 CustomPaging 的数据库,一个名为 Products 的表,然后添加一个名为 GetProductsByPage 的存储过程来检索特定页面的产品。

CREATE DATABASE CustomPaging
GO

Use CustomPaging
GO

CREATE TABLE Products (
    ProductID int IDENTITY (1, 1) NOT NULL ,
    ProductName varchar (50) NOT NULL 
) ON [PRIMARY]
GO

CREATE PROCEDURE GetProductsByPage
@PageNumber int,
@PageSize int
AS
    CREATE TABLE #TempProducts
    (
        ID int IDENTITY PRIMARY KEY,
        ProductID int,
        ProductName varchar(50),
    )

    -- fill the temp table with all the products for the 

    -- specified products retrieved from the Products table

    INSERT INTO #TempProducts 
    (
        ProductID,
        ProductName
    )
    SELECT 
        ProductID,
        ProductName
    FROM Products

    -- declare two variables to calculate the 

    -- range of records to extract for the specified page

    DECLARE @FromID int
    DECLARE @ToID int

    -- calculate the first and last ID of the range of topics we need

    SET @FromID = ((@PageNumber - 1) * @PageSize) + 1
    SET @ToID = @PageNumber * @PageSize

    -- select the page of records

    SELECT ProductID, ProductName FROM #TempProducts 
                   WHERE ID >= @FromID AND ID <= @ToID
GO

在代码中创建数据库和表非常简单,只有创建存储过程 GetProductsByPage 的代码比较长,如果您不熟悉 T-SQL,可能难以理解。它的作用是:首先,它创建一个临时表,该表包含与 Products 表匹配的所有列,以及一个新的标识列。然后,它将 Products 表中的所有记录插入到 temp 表中,这样 temp 表的新标识列就没有缺失值了。接下来,我们声明两个变量来计算请求页面记录的范围。

因此,通过使用此存储过程,我们只需要传递两个参数 PageNumberPageSize(每页记录数)即可检索需要为页面显示的记录。现在,我们将这些记录绑定到 Repeater 控件。

将数据绑定到 Repeater 控件

接下来,我们将把数据绑定到 Repeater 控件。因此,我们需要在我们的 web 窗体中声明一个 Repeater 控件。以下 HTML 代码添加了一个 Repeater 控件和一些用于导航页面的按钮。

<script language="javascript">
    function ChangePage(id)
    {
        // save the page number clicked to the hidden field

        document.all.PageNumber.value = id;
        // call the __doPostBack function to post back 

        // the form and execute the PageClick event

        __doPostBack('PageClick','');
    }
</script>
<body bgcolor="black">
    <form id="Topics" method="post" runat="server">
        <!-- Hidden fields store the current page number 
                               and total pages -->
        <input type="hidden" runat="server" 
                           id="PageNumber" value="1">
        <input type="hidden" runat="server" 
                                id="Pages" value="0">
        <!-- Hidden button to handle the click event when 
                              user clicks on a page link-->
        <asp:button ID="PageClick" OnClick="Page_Click" 
           runat="server" Visible="false"></asp:button>
        <asp:label ID="Info" runat="server"></asp:label>
        <asp:linkbutton ID="FirstPage" 
            runat="server" CommandName="FirstPage" 
            OnCommand="Page_Changed">First</asp:linkbutton>
        <asp:linkbutton ID="PrevPage" runat="server" 
            CommandName="PrevPage" 
            OnCommand="Page_Changed">Prev</asp:linkbutton>
        <asp:label ID="PagesDisplay" 
            runat="server"></asp:label>
        <asp:linkbutton ID="NextPage" 
            runat="server" CommandName="NextPage" 
            OnCommand="Page_Changed">Next</asp:linkbutton>
        <asp:linkbutton ID="LastPage" runat="server" 
            CommandName="LastPage" 
            OnCommand="Page_Changed">Last</asp:linkbutton>
        <br><br>
        <table width="300" style="border: 1 solid gray" align="center">
        <tr>
            <td bgcolor="gray" 
                style="color: white">Product ID</td>
            <td bgcolor="gray" 
                style="color: white">Product Name</td>
        </tr>
        <asp:repeater ID="ProductsRepeater" runat="server">            
            <itemtemplate>
                <tr>
                    <td><%# DataBinder.Eval(Container.DataItem, 
                                            "ProductID") %></td>
                    <td><%# DataBinder.Eval(Container.DataItem, 
                                          "ProductName") %></td>
                </tr>
            </itemtemplate>
        </asp:repeater>
        </table>
    </form>
</body>

我们有两个隐藏字段用于存储当前页码和总页数。然后我们有四个导航按钮:首页、上一页、下一页、末页,它们共享相同的命令,但具有唯一的命令名称。这意味着当这些按钮中的一个被点击时,事件处理程序 Paged_Changed 将被执行。我们还有一个 Label 用于将所有页面显示为链接,这有助于用户更轻松地跳转到其他页面。最后,我们有一个 Repeater 用于显示记录。

请注意,有一个 JavaScript 函数 ChangePage(),当用户单击页码时会调用该函数,将该页码保存到 PageNumber 隐藏字段,然后调用 __doPostBack 函数(由 ASP.NET 自动生成),以提交表单并执行 Page_Click 事件处理程序来刷新页面并显示新记录。

HTML 代码就到这里。现在让我们看一下下面的 C# 代码,了解如何将数据绑定到 Repeater 并实现分页导航器。

<%@ Page Language="C#" ContentType="text/html" 
             ResponseEncoding="iso-8859-1" %>
<%@ import Namespace="System" %>
<%@ import Namespace="System.Data" %>
<%@ import Namespace="System.Data.SqlClient" %>
<script runat="server">
    const int RECORDS_PER_PAGE = 5;
    int totalRecords;
    
    void Page_Load()
    {
        // get the number of records found in the database

        totalRecords = GetProductsCount();
        // calculate and save the number of 

        // pages to the Pages hidden field

        Pages.Value = _
          Math.Ceiling((double)totalRecords/RECORDS_PER_PAGE).ToString();
    
        if (!Page.IsPostBack)
        { 
            // Bind records to repeater

            BindData();
        }
    }

    void BindData()
    {
        int pageNumber = int.Parse(PageNumber.Value);
        int totalPages = int.Parse(Pages.Value);
    
        SqlConnection connection = new 
         SqlConnection("server=(local);database=CustomPaging;uid=sa;pwd=;");
        SqlCommand command = new SqlCommand("GetProductsByPage", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add("@PageNumber", pageNumber);
        command.Parameters.Add("@PageSize", RECORDS_PER_PAGE);
    
        connection.Open();
        DataSet products = new DataSet();
        SqlDataAdapter adapter = new SqlDataAdapter();
        adapter.SelectCommand = command;
        adapter.Fill(products, "Products");
        connection.Close();
    
        // Bind Data to the repeater

        ProductsRepeater.DataSource = products;
        ProductsRepeater.DataBind();
    
        // Display the page links

        PagesDisplay.Text = "";
        for (int i=1; i<=totalPages; i++)
        {
            if (pageNumber != i)
                PagesDisplay.Text += _
                "<a href=\"javascript:ChangePage("+i+")\">"+i+"</a>  ";
            else
                PagesDisplay.Text += "[" + i + "]  ";
        }
    
        // enable/disable the links to navigate through the pages

        FirstPage.Enabled = (pageNumber != 1);
        PrevPage.Enabled = (pageNumber != 1);
        NextPage.Enabled = (pageNumber != totalPages);
        LastPage.Enabled = (pageNumber != totalPages);
    
        Info.Text = totalRecords + " records are found and divided into " 
            + Pages.Value + " pages<br><br>";
    }
    
    // return the number of total records in database

    int GetProductsCount()
    {
        SqlConnection connection = new 
         SqlConnection("server=(local);database=CustomPaging;uid=sa;pwd=;");
        SqlCommand command = new 
         SqlCommand("SELECT Count(*) FROM Products", connection);
    
        connection.Open();
        int count = (int)command.ExecuteScalar();
        connection.Close();
    
        return count;
    }
    
    // execute when user clicks on the next/prev/first/last button

    void Page_Changed(object sender, CommandEventArgs e)
    {
        switch (e.CommandName)
        {
            case "FirstPage":
                PageNumber.Value = "1";
                break;
            case "PrevPage":
                PageNumber.Value = _
                  (int.Parse(PageNumber.Value) -1).ToString();
                break;
            case "NextPage":
                PageNumber.Value = _
                  (int.Parse(PageNumber.Value) +1).ToString();
                break;
            case "LastPage":
                PageNumber.Value = Pages.Value;
                break;
        }
    
        // rebind data

        BindData();
    }
    
    // execute when user clicks on the page link

    void Page_Click(object sender, System.EventArgs e)
    {
        // rebind data

        BindData();
    }
</script>

当页面加载时,它通过调用 GetProductsCount() 方法获取总记录数,然后计算页数并将其保存到表单的隐藏字段中。接下来,它调用 BindData() 方法将数据绑定到 Repeater 控件,并在页面未回发时显示页面链接,因为回发时不需要重新绑定数据。

上面的代码有两个事件处理程序,第一个是 Page_Click,它只重新绑定数据到 Repeater;另一个是 Page_Changed,当用户单击 4 个导航按钮中的一个时执行。它通过命令名称检测哪个按钮被点击,并将要显示的页码保存到隐藏字段,然后重新绑定数据。

这就是实现我们自定义数据分页所需的所有内容。感谢您的阅读。

© . All rights reserved.