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

在 ASP.NET 2.0 中使用 DataList 控件实现高效数据分页

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (25投票s)

2006年5月12日

4分钟阅读

viewsIcon

233123

downloadIcon

9691

通过为照片库实现高效的数据分页来扩展个人网站入门工具包。

Sample Image - paging1.jpg

引言

本文的目的是描述如何在 ASP.NET 2.0 中使用 DataList 控件实现自定义分页。我将向您展示如何检索部分数据,使用 DataList 显示数据,并提供导航界面。在我阅读了 Scott Guthrie 的题为《使用 ASP.NET 2.0 DataList 控件和 ObjectDataSource 进行高效数据分页》的文章后,我决定撰写本文。个人网站入门工具包中的 Photos.aspx 页面是一个完美的例子,因为它使用了 DataListObjectDataSource。这个入门工具包可以从 Microsoft 下载。

问题

个人网站入门工具包中的 Photos.aspx 使用 DataList 显示绑定到 ObjectDataSource 的照片缩略图。ObjectDataSource 使用 PhotoManager 类的 GetPhotos 方法检索属于选定相册的所有照片记录。缩略图(每条照片记录一个)显示在页面上,每行四个。您可以看到,每页最多只能显示 8 或 12 张缩略图,如果还有更多,您将需要向下滚动才能看到剩余的。如果您每个相册的照片少于 100 张,这通常不是大问题。但如果您有 200 张或更多照片,您就会意识到分页解决方案是可取的。

正如我前面提到的,我从 Scott Guthrie 的文章中借鉴了一些想法。在他的示例中,分页 UI 包括“上一页”和“下一页”链接,以及显示当前页和总页数。人们会注意到,通过添加跳转到特定页面或转到第一页或最后一页的功能,可以轻松改进它。

解决方案

我们将在 photos.aspx 网页中实现自定义分页。我们必须在 PhotoManager 类中创建一个新的 GetPhotos 方法,该方法将调用新的存储过程 GetPhotosByPageIndex。此存储过程将根据 pageIndex 和行数检索部分数据。在分页 UI 方面,我决定通过添加页码链接和一个“查看全部”链接来显示所有数据来扩展它。为了显示用于导航记录的分页 UI,我们必须显示一些按钮或链接,以及可选的页码链接和“查看全部”链接。这是一个典型在线商店中分页的示例。

更改列表

  • photos.aspx
  • GetPhotosByPageIndex 存储过程
  • photomanager.vb
  • photos.aspx.vb
  • default.skin

Photos.aspx

<%@ Page Language="VB" MasterPageFile="~/Default.master" 
    Title="Your Name Here | Photos"
    CodeFile="Photos.aspx.vb" 
    Inherits="Photos_aspx" %>

<asp:content id="Content1" 
     contentplaceholderid="Main" runat="server">

    <div class="shim solid"></div> 

    <div class="page" id="photos">
        <div class="buttonbar buttonbar-top">
            <a href="Albums.aspx">
              <asp:image ID="Image1" runat="Server" skinid="gallery" />
            </a>
        </div>
            <asp:DataList ID="DataList1" runat="Server" 
              cssclass="view"    dataSourceID="ObjectDataSource1" 
              repeatColumns="4" repeatdirection="Horizontal" 
              onitemdatabound="DataList1_ItemDataBound" 
              EnableViewState="false">
            <ItemTemplate>
                <table border="0" cellpadding="0" 
                       cellspacing="0" class="photo-frame">
                    <tr>
                        <td class="topx--"></td>
                        <td class="top-x-"></td>
                        <td class="top--x"></td>
                    </tr>
                    <tr>
                        <td class="midx--"></td>
                        <td><a id="DetailLink" runat="server">
                            <img src="Handler.ashx?PhotoID=<%# 
                                      Eval("PhotoID") %>&Size=S" class="photo_198" 
                              style="border:4px solid white" 
                              alt='Thumbnail of Photo Number 
                                   <%# Eval("PhotoID") %>' />
                            </a>
                        </td>
                        <td class="mid--x"></td>
                    </tr>
                    <tr>
                        <td class="botx--"></td>
                        <td class="bot-x-"></td>
                        <td class="bot--x"></td>
                    </tr>
                </table>
                <p><%# Server.HtmlEncode(Eval("Caption").ToString()) %></p>
            </ItemTemplate>
            <FooterTemplate>
            </FooterTemplate>
        </asp:DataList>
                <div id="paging" align="center">
                    <asp:Label ID="PagerLocation" runat="server" />  
                    <asp:PlaceHolder ID="ViewAll" runat="server"></asp:PlaceHolder>
                    <a id="PrevPage" runat="server">
                        <asp:image runat="Server" id="PrevImage" 
                              AlternateText="Previous page" skinid="prevpage"/></a>
                    <asp:PlaceHolder ID="PageLinks" runat="server"></asp:PlaceHolder>
                    <a id="NextPage" runat="server">
                        <asp:image runat="Server" id="NextImage" 
                           AlternateText="Next page" skinid="nextpage"/></a>
                </div>            
        <asp:panel id="Panel1" runat="server" visible="false" 
               CssClass="nullpanel">There are currently no pictures 
               in this album.</asp:panel>
        <div class="buttonbar">
            <a href="Albums.aspx"><asp:image id="gallery" 
               runat="Server" skinid="gallery" /></a>
        </div>
    </div>    
    <asp:ObjectDataSource ID="ObjectDataSource1" 
          Runat="server" TypeName="PhotoManager" 
          SelectMethod="GetPhotos">
        <SelectParameters>
            <asp:QueryStringParameter Name="AlbumID" 
                 Type="Int32" QueryStringField="AlbumID" DefaultValue="0"/>
            <asp:QueryStringParameter Name="PageIndex" 
                 QueryStringField="PageIndex" DefaultValue="0" />
            <asp:QueryStringParameter Name="NumRows" 
                 QueryStringField="NumRows" DefaultValue="8" />
            <asp:Parameter Name="PhotoCount" 
                 Direction="Output" Type="Int32" />
        </SelectParameters>
    </asp:ObjectDataSource>
</asp:content>

photos.aspx 网页包含一个 DataList,其中包含用于渲染每个缩略图的 ItemTemplate、用于显示导航链接的 div 元素以及一个 ObjectDataSourceItemTemplate 包含一个超链接 DetailLink,它将在 DataListItemDataBound 事件期间创建。分页 div 元素将包含一个 PagerLocation 标签、一个 ViewAll 占位符、一个“上一页”图像按钮、一个用于页码链接的占位符以及一个“下一页”图像按钮。ObjectDatasource 包含额外的参数,例如 PageIndexNumRowsPhotoCount,用于创建分页 UI。您可以通过设置 NumRows 参数的 DefaultValue 来更改每页的缩略图数量。在此示例中,每页将有 8 个缩略图。PhotoCount 参数将包含显示在分页 UI 中的照片数量。

GetPhotosByPageIndex 存储过程

数据库包含两个表:Albums 和 Photos。

CREATE PROCEDURE dbo.GetPhotosByPageIndex
(
    @AlbumID int,
    @IsPublic bit,
    @PageIndex INT,
    @NumRows INT,
    @PhotoCount INT OUTPUT
)    
AS
BEGIN
    SET NOCOUNT ON
    /* 
       The below statement enable returning 
       the Total Photo Count for the AlbumID
       as output paramster to our SPROC. This enables 
       us to avoid having to make a separate call to the 
       database to retrieve them, and can help 
       improve performance quite a bit
    */
    
    SELECT @PhotoCount=(SELECT COUNT(*) FROM Photos 
           where Photos.AlbumID=@AlbumID)
    
    Declare @startRowIndex INT;
    set @startRowIndex = (@PageIndex * @NumRows) + 1;

    With PhotoEntries as (
        SELECT ROW_NUMBER() OVER (ORDER BY PhotoID ASC) as Row, 
               PhotoID, Photos.AlbumID, Photos.Caption
        FROM [Photos] LEFT JOIN [Albums]
            ON [Albums].[AlbumID] = [Photos].[AlbumID] 
        WHERE [Photos].[AlbumID] = @AlbumID
              AND ([Albums].[IsPublic] = @IsPublic 
              OR [Albums].[IsPublic] = 1)
    )

    SELECT PhotoID, AlbumID, Caption
    FROM PhotoEntries
    WHERE Row between 
    @startRowIndex and @StartRowIndex+@NumRows-1
END

此存储过程仅适用于 SQL Server 2005(Express 或 Standard)。它更高效,因为我们只检索我们感兴趣的记录。请注意,它利用了 SQL Server 2005 中的新 ROW_NUMBER() 函数。它将照片数量作为 PhotoCount 输出参数返回。

PhotoManager.vb

Public Shared Function GetPhotos(ByVal albumid As Integer, _
       ByVal PageIndex As Integer, ByVal NumRows As Integer, _
       ByRef PhotoCount As Integer) As List(Of Photo)

        Using connection As New _
              SqlConnection(ConfigurationManager.ConnectionStrings(
                            "Personal").ConnectionString)
            Using command As New SqlCommand("GetPhotosByPageIndex", connection)
                command.CommandType = CommandType.StoredProcedure
                command.Parameters.Add(New SqlParameter("@AlbumID", albumid))
                Dim Filter As Boolean = _
                  Not (HttpContext.Current.User.IsInRole("Friends") _
                  Or HttpContext.Current.User.IsInRole("Administrators"))
                command.Parameters.Add(New SqlParameter("@IsPublic", filter))
                command.Parameters.Add(New SqlParameter("@PageIndex", PageIndex))
                command.Parameters.Add(New SqlParameter("@NumRows", NumRows))
                Dim parm As SqlParameter = _
                   command.Parameters.Add("@PhotoCount", SqlDbType.Int, 4)
                parm.Direction = ParameterDirection.Output
                connection.Open()
                Dim list As List(Of Photo) = New List(Of Photo)
                Dim reader As SqlDataReader = command.ExecuteReader()
                While reader.Read()
                    Dim temp As New Photo(CInt(reader("PhotoID")), _
                        CInt(reader("AlbumID")), CStr(reader("Caption")))
                    list.Add(temp)
                End While
                connection.Close()
                PhotoCount = Convert.ToInt32(parm.Value)
                Return list
            End Using
        End Using
    End Function

此方法在 ObjectDataSourceSelectMethod 属性中指定。它用于通过传递相册 ID、IsPublicPageIndexNumRows 来调用 GetPhotosByPageIndex 存储过程。它将返回照片列表和照片总数。

Photos.aspx.vb

Partial Class Photos_aspx _
        Inherits System.Web.UI.Page

    Dim infinity As Integer = 9999
    Dim defaultPageSize As Integer
    Dim albumID As Integer = 1
    Dim pageIndex As Integer = 0
    Dim pageSize As Integer = 0

    Protected Sub Page_Load(ByVal sender As Object, _
              ByVal e As System.EventArgs) Handles Me.Load
        albumID = Convert.ToInt32(Request.QueryString("albumID"))
        pageIndex = Convert.ToInt32(Request.QueryString("pageIndex"))
        defaultPageSize = _
          Convert.ToInt32(
            ObjectDataSource1.SelectParameters("NumRows").DefaultValue)
        pageSize = defaultPageSize
        If Request.QueryString("NumRows") <> Nothing Then
            pageSize = Convert.ToInt32(Request.QueryString("NumRows"))
        End If
    End Sub

    Protected Sub DataList1_ItemDataBound(ByVal sender As Object, _
                  ByVal e As DataListItemEventArgs)
        If (e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = _
                                    ListItemType.AlternatingItem) Then
            Dim Url As String = "Details.aspx?AlbumID={0}&Page={1}"
            Dim aLink As HtmlAnchor = _
                      CType(e.Item.FindControl("DetailLink"), HtmlAnchor)
            aLink.HRef = String.Format(Url, albumID, _
                         defaultPageSize * pageIndex + e.Item.ItemIndex)
        End If
        If (e.Item.ItemType = ListItemType.Footer) Then
            If (DataList1.Items.Count = 0) Then
                Panel1.Visible = True
            End If
        End If
    End Sub

    Protected Sub ObjectDataSource1_Selected(ByVal sender As Object, _
           ByVal e As System.Web.UI.WebControls.ObjectDataSourceStatusEventArgs) _
           Handles ObjectDataSource1.Selected
        ' Retrieve output parameter values returned from

        ' calling the "ProductsTableAdapter.GetProductsByCategoryId" 

        ' method invoked by the ObjectDataSource control

        Dim photoCount As Integer = _
            Convert.ToInt32(e.OutputParameters("PhotoCount"))

        ' Update various page elements with data values

        UpdatePagerLocation(pageIndex, pageSize, photoCount)
        UpdateNextPrevLinks(albumID, pageIndex, pageSize, photoCount)
        UpdatePager(albumID, pageIndex, pageSize, photoCount)
    End Sub

    Protected Sub UpdatePagerLocation(ByVal pageIndex As Integer, _
                  ByVal pageSize As Integer, ByVal photoCount As Integer)
        Dim currentStartRow As Integer = (pageIndex * pageSize) + 1
        Dim currentEndRow As Integer = (pageIndex * pageSize) + pageSize

        If (currentEndRow > photoCount) Then
            currentEndRow = photoCount
        End If
        Dim pageCount As Integer
        If photoCount Mod pageSize = 0 Then
            pageCount = photoCount / pageSize
        Else
            pageCount = photoCount \ pageSize + 1
        End If

        PagerLocation.Text = String.Format("Page {0} of {1}", _
                             pageIndex + 1, pageCount)
        If pageSize = infinity Then
            PagerLocation.Visible = False
        End If
    End Sub

    Protected Sub UpdateNextPrevLinks(ByVal AlbumID As Integer, _
              ByVal pageIndex As Integer, ByVal pageSize As Integer, _
              ByVal photoCount As Integer)
        Dim navigationFormat As String = _
            "photos.aspx?AlbumID={0}&pageIndex={1}&NumRows={2}"

        PrevPage.HRef = String.Format(navigationFormat, _
                        AlbumID, pageIndex - 1, defaultPageSize)
        PrevPage.Visible = (pageIndex > 0)

        NextPage.HRef = String.Format(navigationFormat, AlbumID, _
                        pageIndex + 1, defaultPageSize)
        NextPage.Visible = (pageIndex + 1) * pageSize < photoCount
    End Sub

    Protected Sub UpdatePager(ByVal AlbumID As Integer, _
              ByVal pageIndex As Integer, ByVal pageSize _
              As Integer, ByVal photoCount As Integer)
        Dim navigationFormat As String = _
              "photos.aspx?AlbumID={0}&pageIndex={1}&NumRows={2}"
        Dim link As HyperLink
        Dim literal As LiteralControl
        Dim lb As Label
        Dim count As Integer = 0
        Dim index As Integer = 0
        If pageSize = infinity Then
            pageIndex = -1
        End If
        Do While count < photoCount
            If index = pageIndex Then
                lb = New Label
                lb.Font.Bold = True
                lb.Text = index + 1
                PageLinks.Controls.Add(lb)
            Else
                link = New HyperLink
                link.NavigateUrl = String.Format(navigationFormat, _
                                   AlbumID, index, defaultPageSize)
                link.Text = index + 1
                PageLinks.Controls.Add(link)
            End If
            literal = New LiteralControl
            literal.Text = " "
            PageLinks.Controls.Add(literal)
            index += 1
            count += defaultPageSize
        Loop
    End Sub

    Protected Sub ObjectDataSource1_Selecting(ByVal sender As Object, _
         ByVal e As System.Web.UI.WebControls.ObjectDataSourceSelectingEventArgs) _
         Handles ObjectDataSource1.Selecting
        Dim navigationFormat As String = _
              "photos.aspx?AlbumID={0}&pageIndex={1}&NumRows={2}"
        If pageSize = infinity Then
            Dim lb As New Label
            lb.Text = "View All"
            ViewAll.Controls.Add(lb)
        Else
            Dim link As New HyperLink
            link.NavigateUrl = _
                 String.Format(navigationFormat, albumID, 0, infinity)
            link.Text = "View All"
            ViewAll.Controls.Add(link)
        End If
    End Sub

End Class

Page_Load 事件中,我检索各种查询字符串参数。在 DataList1_ItemDataBound 事件中,名为 DetailLink 的超链接会为每个缩略图动态更新。在 ObjectDataSource1_Selected 事件中,我调用各种方法来渲染分页 UI。UpdatePagerLocation 方法用于显示当前页与总页数。UpdateNextPrevLinks 用于渲染“上一页”和“下一页”按钮。UpdatePager 用于生成页码超链接。在 ObjectDataSource1_Selecting 事件中,我渲染“查看全部”超链接或标签。

Default.skin

需要为 White 主题将以下两行添加到 default.skin

<asp:Image runat="server" 
           ImageUrl="images/button-prev.gif" skinid="prevpage"/>
<asp:Image runat="server" 
           ImageUrl="images/button-next.gif" skinid="nextpage"/>

需要为 Black 主题将以下两行添加到 default.skin

<asp:Image runat="server" 
           ImageUrl="images/button-prev.jpg" skinid="prevpage"/> 
<asp:Image runat="server" 
           ImageUrl="images/button-next.jpg" skinid="nextpage"/>

个人网站入门工具包可以在这里找到。

历史

无。

© . All rights reserved.