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






4.85/5 (25投票s)
2006年5月12日
4分钟阅读

233123

9691
通过为照片库实现高效的数据分页来扩展个人网站入门工具包。
引言
本文的目的是描述如何在 ASP.NET 2.0 中使用 DataList
控件实现自定义分页。我将向您展示如何检索部分数据,使用 DataList
显示数据,并提供导航界面。在我阅读了 Scott Guthrie 的题为《使用 ASP.NET 2.0 DataList 控件和 ObjectDataSource 进行高效数据分页》的文章后,我决定撰写本文。个人网站入门工具包中的 Photos.aspx 页面是一个完美的例子,因为它使用了 DataList
和 ObjectDataSource
。这个入门工具包可以从 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
元素以及一个 ObjectDataSource
。ItemTemplate
包含一个超链接 DetailLink
,它将在 DataList
的 ItemDataBound
事件期间创建。分页 div
元素将包含一个 PagerLocation
标签、一个 ViewAll 占位符、一个“上一页”图像按钮、一个用于页码链接的占位符以及一个“下一页”图像按钮。ObjectDatasource
包含额外的参数,例如 PageIndex
、NumRows
和 PhotoCount
,用于创建分页 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
此方法在 ObjectDataSource
的 SelectMethod
属性中指定。它用于通过传递相册 ID、IsPublic
、PageIndex
和 NumRows
来调用 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"/>
个人网站入门工具包可以在这里找到。
历史
无。