Jag bloggar numera på http://blog.dileno.com ».

Paging i ASP.NET med PagedDataSource

en ASP.NET-artikel av Martin S., publicerad den 12 juni 2005
Med ASP.NET kom möjligheten att enkelt använda sig av paging om man valde att presentera data via en DataGrid. Dock är DataGrid ett tungt objekt som inte direkt är prestandavänligt, och vill man bara använda sig av dess pagingegenskaper så finns det faktiskt alternativ – med hjälp av PagedDataSource-klassen kan du i .NET skriva en egen pagingfunktion utan att använda dig av en DataGrid. Jag visar hur.

PagedDataSource-klassen och några egenskaper


Klassen vi ska använda sig av är alltså PagedDataSource, som är en del av namnområdet System.Web.UI.WebControls. Du som har använt dig av DataGrid tidigare känner troligtvis igen dig i PagedDataSource, då ett flertal av de pagingegenskaper som återfinns hos en DataGrid även är representerade i PagedDataSource.

För att skapa en instans av klassen behövs endast fyra rader kod:

dim pagedData as new PagedDataSource
pagedData.DataSource = theData
pagedData.AllowPaging = true
pagedData.PageSize = 10

Man skapar först instansen (pagedData), anger sedan datakällan som innehåller den data vill visa. För att kunna nyttja paging måste egenskapen AllowPaging sättas till true (den är satt till false som default annars, då fungerar inte pagingen). Slutligen anger man hur många poster som ska visas per sida (10 st).

Läs mer om PagedDataSource-klassen och dess egenskaper på MSDN: PagedDataSource class @ MSDN

Presentera data - paging.aspx


Exempelsidan, paging.aspx, ska se ut så här:

<%@ Page Language="VB" Inherits="db_paging" Src="paging.vb" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Exempel: paging i ASP.NET med PagedDataSource</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<form id="paging" runat="server">

<asp:Repeater id="main" runat="server">
<ItemTemplate><%# Container.DataItem("Namn") %></ItemTemplate>
<SeparatorTemplate><br /></SeparatorTemplate>
</asp:Repeater>
<p><asp:literal id="num_paging" runat="server" /></p>

<asp:literal id="lit_error1" runat="server" />

</form>
</body>
</html>

Första raden talar i sedvanlig ordning om vilket språk man kodar i (VB.NET), vilken klass som ska ärvas (db_paging) från vilken codebehind-fil (paging.vb).

På sidan har vi en repeater för att visa datan, en literal för att skriva ut pagingen och en literal till som används för att skriva ut felmeddelanden.

Codebehind - paging.vb


I codebehinfilen, paging.vb, finns det intressanta. I sedvanlig ordning måste man importera de namnområden som man behöver. System.Data.OleDb, System.Text och Microsoft.VisualBasic är namnområden jag kommer använda mig av förutom dem man vanligtvis importerar.

När en sida laddas körs två procedurer för att visa data från en databas och skapa en pagingfunktion; returnData() och NumericPages().

För att plocka ut data från en databas använder jag mig av en funktion som får agera datakälla när man senare ska fylla repeatern på paging.aspx med den data man vill visa. Så här ser funktionen för att plocka ut data ur databasen ut:

function getdata() as dataTable
dim SQL as string = "SELECT namn FROM tbl_paging ORDER BY id"
dim cnstr as string = "Provider=Microsoft.Jet.Oledb.4.0; data source=" & Server.MapPath("db_paging.mdb")

dim cn as new OledbConnection(cnstr)
dim Oda as new OledbDataAdapter(SQL, cn)
dim ds as new dataset()

try
Oda.fill(ds, "tbl")
return ds.Tables("tbl").copy
catch ex as exception
lit_error.text = "Error: " & ex.Message
finally
Oda.Dispose()
end try

end function

Till att börja med talar vi om att funktionen ska returneras som en DataTable. Sedan har vi SQL-frågan, anslutningen till databasen och en OleDbDataAdapter som behövs för att fylla ett DataSet med den informationen vi vill ha. För att fånga upp eventuella fel som kan uppstå använder jag mig av ett try.. catch.. finally-block.

I proceduren returnData får funktionen sedan agera datakälla.

sub returnData()

pagedData.datasource = getdata().defaultView
pagedData.allowPaging = True
pagedData.Pagesize = 10

recscount = pagedData.dataSourceCount

try
if Len(request.querystring("page"))=0 then
pagedData.CurrentPageIndex = 0
else
if not isNumeric(request.querystring("page")) orelse cint(request.querystring("page"))<0 then
lit_error.Text = "Error: adressen till sidan måste vara numerisk och över 0, inte " &_
"" & request.querystring("page") & """."
else
pagedData.CurrentPageIndex = cint(request.querystring("page"))
end if
end if

catch ex as exception
pagedData.currentPageindex = 0
lit_error.Text = "Error: " & ex.Message
end try

main.datasource = pagedData
main.databind()

end sub

De tre första raderna vet vi redan vad de gör – anger datakälla, anger att paging ska tillåtas och att det ska visas 10 poster per sida. Sedan har vi variabeln recscount i vilken vi lagrar antalet poster som finns i datakällan. Detta kommer till nytta senare. Ett try.. catch-block till för att fånga upp fel och en nästlad villkorssats för att sätta index beroende på var man är i pagingen (pagedData.CurrentPageIndex och pagedData.CurrentPageIndex = cint(request.querystring("page"))).

Jag har lagt in en koll för att se efter om request.querystring("page") returnerar en siffra eller inte och om siffran är större än 0. Om den inte gör det så uppstår ett fel och pagingen avbryts, om det returneras en siffra som är 0 eller högre så fortsätter vi.

Om siffran som returneras är lägre än 0 så sker ett error – det finns inget lägre index än 0. Därav kollen med orelse cint(request.querystring("page"))<0.

Slutligen anger vi pagedData som dagakälla till repeatern main på paging.aspx och binder datan till repeatern.

Skriva ut pagingen


Sist men inte minst har vi proceduren för att skriva ut pagingen på sidan. NumericPages() ser till detta. Kod:

sub NumericPages()

if pagedData.currentPageindex > pagedData.PageCount then
lit_error.Text = "Error! Sidan " & request.querystring("page") & " finns inte i pagingen."
else
dim sb as new StringBuilder()
for PageCounter = 1 To pagedData.PageCount

if not pagedData.CurrentPageIndex = PageCounter-1 then
sb.append("<a href=""paging.aspx?page=" & Pagecounter-1 & """>" & Pagecounter & "</a> ")
else
if not recscount <= pagedData.Pagesize then
sb.append(PageCounter & " ")
end if
end if

next

num_paging.Text = sb.toString()
end if

end sub

Om en användare skulle ha skrivit i en egen pagingsiffra som har ett index som är högre än det högsta index som finns (det vill säga om det finns 10 sidor i pagingen och användaren skrivit i page=11 i adressen för sidan) så skriver vi ut ett felmeddelande. Det är med Med pagedData.PageCount räknar vi antalet sidor som finns i pagingen.

Om allt är som det ska skapar vi en StringBuilder och sedan loopar vi igenom alla sidor i pagingen ((for PageCounter = 1 To pagedData.PageCount)) och lägger till text i StringBuildern för varje loop. Beroende på om man är på en viss sida eller inte så visas pagingen för den sidan inte som en länk, utan enbart som text. Slutligen säger vi till att datan i StringBuildern ska läggas till i literalen num_paging som finns på paging.aspx.

Kodlistning paging.vb


paging.vb ser i sin helhet ut så här:

Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Data
Imports System.Data.OleDb
Imports System.Text
Imports Microsoft.VisualBasic


public class db_paging : inherits Page

dim pagedData as new pagedDataSource
dim pagecounter, recscount as integer



protected withevents main as repeater
protected withevents lit_error, num_paging as literal


'proceduren som körs varje gång sidan laddas
sub page_load(sender as object, e as eventargs)

returnData()
NumericPages()

end sub


'funktionen för att plocka ut info ur databasen och fylla en OledbDataAdapter med den
function getdata() as dataTable

dim SQL as string = "SELECT id,namn FROM tbl_paging ORDER BY id"
dim cnstr as string = "Provider=Microsoft.Jet.Oledb.4.0;data source=" & Server.MapPath("db_paging.mdb")

dim cn as new OledbConnection(cnstr)
dim Oda as new OledbDataAdapter(SQL, cn) 'skapa en OledbDataAdapter och fyll DataSetet
dim ds as new dataset()

try
Oda.fill(ds, "tbl")
return ds.Tables("tbl").copy
catch ex as exception
lit_error.text = "Error: " & ex.Message
finally
Oda.Dispose()
end try

end function


'proceduren där parametrar för själva pagingen ställs in
sub returnData()

'datakälla till pagingen (funktionen ovan), tillåt paging, ange antalet poster per sida
pagedData.DataSource = getdata().defaultView
pagedData.AllowPaging = True
pagedData.PageSize = 10 'ändra siffran här till antalet poster du vill ha per sida

'räkna antalet poster i datakällan
recscount = pagedData.dataSourceCount

'felhantering med try..catch
try
if len(request.querystring("page"))=0 then
pagedData.CurrentPageIndex = 0
else
if not isNumeric(request.querystring("page")) orelse cint(request.querystring("page"))<0 then
lit_error.Text = "Error: adressen till sidan måste vara numerisk och över 0, inte " &_
"" & request.querystring("page") & """."
else
pagedData.currentPageindex = cint(request.querystring("page"))
end if
end if

'fånga upp eventuella fel på sidan och skriv ut felmeddelandet
catch ex as exception
pagedData.CurrentPageIndex = 0
lit_error.Text = "Error: " & ex.Message
end try

'bind data till repeatern på huvudsidan
main.datasource = pagedData
main.databind()

end sub


'proceduren för att skriva ut (den numeriska) sidnavigeringen
sub NumericPages()

'felhantering om användaren skriver i ett pagingtal i adressen som inte finns

if pagedData.currentPageindex > pagedData.Pagecount then
lit_error.Text = "Error! Sidan " & request.querystring("page") & " finns inte i pagingen."
else

'skapa en stringBuilder för att kunna for .. next-loopen nedan ska fungera
dim sb as new stringBuilder()

'for ... next-loopen för att skriva ut sidnavigeringen
for Pagecounter = 1 To pagedData.Pagecount
if not pagedData.currentPageindex = Pagecounter-1 then
sb.append("<a href=""paging.aspx?page=" & Pagecounter-1 & """>" & Pagecounter & "</a> ")
else
if not recscount <= pagedData.Pagesize then
sb.append(Pagecounter & " ")
end if
end if
next

'skriv ut stringBuilderns data
num_paging.Text = sb.tostring()

end if

end sub

end class

Ladda ner filerna


Du kan ladda ner paging.aspx, paging.vb samt databasen här: Ladda ner exempelfilerna (zip-fil, 13.5 kb)

Se exempel


Se exempel på hur pagingen kan se ut: Exempel: paging i ASP.NET

Läs mer om PagedDataSource och paging


Du kan läsa mer om PagedDataSource på MSDN. Läs mer om egenskaperna här: PagedDataSource members.

Kom ihåg - behöver du inte använda dig av en DataGrid så är PagedDataSource tillsammans med en repeater alldeles utmärkt. Enkelt att bygga ut och inte alltför prestandakrävande – och dessutom med pagingfunktioner fullt likvärdiga dem som finns i DataGriden.