Desenvolvimento - Visual Basic .NET

Paginando Dados utilizando DataList

Veremos neste artigo como resgatar os dados da base de dados através de uma Stored Procedure, limitando os dados da página a ser exibida para proporcionar performance, e retornando os dados para uma aplicação ASP.NET, populando um controle DataList, controlando e exibindo a paginação.

por Israel Aéce



function DemoOnline() { window.open("http://www.projetando.net/Articles/DemoOnline.aspx?ArticleID=34", "", "width=750, height=580, scrollbars=yes, resizable=yes"); }

Ao contrário do DataGrid, o DataList não nos fornece wizards para a paginação do mesmo, tendo assim que criarmos uma customizada, recuperando e exibindo os dados da página que o usuário desejar visualizar. Há momentos em que o result set da base de dados é muito grande, ou seja, é retornado uma grande quantidade de registros, o que pode custar muito caro para o cliente, tendo que receber todos os registros, e exibir apenas os registros que se enquadram na página atual, e limitando ao número de registros por páginas.

O grande problema, é que retornamos ao cliente registros indispensáveis para página corrente, o que de certa forma, causa um grande tráfego na rede para no final ser desconsiderado. Sendo assim, retornar todos os registros, e armazenar em uma variável de sessão, também é muito custoso, talvez quando o acesso é mínimo, não se perceba, mas quando a quantidade de usuários requisitando a mesma informação, isso poderá ocasionar sérios problemas de performance.

Tendo este cenário e/ou problema, a solução seria retornarmos da base de dados somente os registros correspondentes a página qual o usuário requerer. Além de diminuir o tráfego de dados na rede, isso nos proporcionará um grande ganho de performance. O artigo mostrará como retornar somente os registros da base de dados qual se enquadram com a página que o usuário requerer. Isso será feito através de uma Stored Procedure, utilizando a tabela Customers da base de dados Northwind como exemplo. Através de um SqlDataReader, recuperamos o result set retornado pela Stored Procedure e populamos o controle DataList.

O primeiro passo e construirmos a Stored Procedure para nos retornar os registros que necessitamos. Com isso, precisamos informar a ela a pagina requerida pelo Usuário, a quantidade de registros à ser exibido por página e também criarmos um parâmetro de OUTPUT para sabermos a quantidade total de registros, que é necessário para efetuar os cálculos no cliente, para que seja possível manipular corretamente a navegação entre as páginas. Abaixo a Stored Procedure responsável por devolver apenas os registros que necessitamos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
CREATE PROCEDURE ResgatarClientes
@QtdePagina As Int,
@PagAtual As Int,
@TotalClientes As Int OUTPUT
AS
DECLARE @ContactName As Varchar(50)
DECLARE @City As Varchar(50)
DECLARE @QtdeInicial As Int
DECLARE @Contador As Int
SET @QtdeInicial = 0
SET @Contador = 0
SET NOCOUNT ON
CREATE TABLE #ClientesTemp
(
ContactName Varchar(50)
City Varchar(50)
)
DECLARE curPaginacaoClientes CURSOR FAST_FORWARD FOR
SELECT ContactName, City FROM Customers ORDER BY ContactNameASC
OPEN curPaginacaoClientes
FETCH NEXT FROM curPaginacaoClientes
INTO @ContactName, @City
WHILE @@FETCH_STATUS = 0
BEGIN
IF @QtdeInicial>= (@PagAtual * @QtdePagina) - @QtdePagina
BEGIN
INSERT INTO #ClientesTemp VALUES(@ContactName, @City)
SET @Contador = @Contador + 1
IF @Contador>= @QtdePagina
BREAK
END
SET @QtdeInicial = @QtdeInicial + 1
FETCH NEXT FROM curPaginacaoClientes
INTO @ContactName, @City
END
CLOSE curPaginacaoClientes
DEALLOCATE curPaginacaoClientes
SELECT ContactName, City FROM #ClientesTemp
DROP TABLE #ClientesTemp
SET NOCOUNT OFF
SET @TotalClientes = (SELECT COUNT(CustomerID) FROM Customers)
GO

Código 1 - Stored Procedure "ResgataClientes".


Como vemos no código acima, a Stored Procedure recebe dois parâmetros que é a página requisitada pelo Usuário e a quantidade de registros por página. Além disso, como dito anteriormente, existe um parâmetro de OUTPUT chamado @TotalClientes que e responsável por retornar a quantidade de Clientes para que seja possível efetuar os cálculos e assim exibindo corretamente a navegação da paginação.

Podemos ver também que é criado uma tabela temporária na linha 19, chamada #ClientesTemp, que é utilizada posteriormente, sendo populada com os registros necessários. Depois disso é criado um Cursor na linha 25 e através dele percorremos a tabela Customers e preenchemos a tabela temporária, anteriormente criada. Feito isso, fechamos o Cursor e o retiramos da memória, e retornamos ao cliente o resultado de um SELECT efetuado nesta tabela temporária na linha 51 e depois apagamos esta tabela (linha 53). E finalmente, apenas atribuimos ao parâmetro de saída a quantidade de registros da tabela Customers na linha 57.

Ainda é possível testá-la no Query Analyser, através do seguinte código:

1
2
3
DECLARE @TotalRegistros As Int
EXECUTE ResgatarClientes 10, [N], @TotalClientes = @TotalRegistros OUTPUT
PRINT @TotalRegistros

Código 2 - Testando a Stored Procedure "ResgataClientes" no Query Analyser.


Como vemos no código acima, o número 10 é a quantidade de registros por página e o [N] refere-se a página que o cliente está requerendo. Feito isso, o próximo passo é partir para a aplicação ASP.NET, que irá utilizar esta Stored Procedure para popular e paginar os dados em um controle do tipo DataList. Devemos apenas arrastar em nosso WebForm o DataList e quatro controles do tipo LinkButton, que serão responsáveis pela navegação do usuário. Vamos incluir em nosso WebForm um Data Member que será responsável por armazenar a quantidade de registros por páginas:

1
Private _totalRegistrosPagina As Integer = 10

Código 3 - Definindo através de um Data Member a quantidade de registros por página.


Logo, temos que criar um procedimento que será responsável por popular o DataList. Esta função se chamará CarregaGrid, e terá como parâmetro um valor inteiro representando a página que o usuário está requerendo. Vejamos abaixo o código deste procedimento:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Private Sub CarregaGrid(ByVal paginaAtual As Integer)
Dim conn As New SqlConnection("CONNECTION_STRING")
Dim dr As SqlDataReader
Dim cmd As New SqlCommand("ResgatarClientes", conn)
cmd.CommandType = CommandType.StoredProcedure
Dim paramQtdeRegistroPagina As SqlParameter
paramQtdeRegistroPagina = New SqlParameter("@QtdePagina", SqlDbType.Int)
paramQtdeRegistroPagina.Value = Me._totalRegistrosPagina
cmd.Parameters.Add(paramQtdeRegistroPagina)
Dim paramPaginaAtual As SqlParameter
paramPaginaAtual = New SqlParameter("@PagAtual", SqlDbType.Int)
paramPaginaAtual.Value = paginaAtual
cmd.Parameters.Add(paramPaginaAtual)
Dim paramTotalRegistros As SqlParameter
paramTotalRegistros = New SqlParameter("@TotalClientes", SqlDbType.Int)
paramTotalRegistros.Direction = ParameterDirection.Output
cmd.Parameters.Add(paramTotalRegistros)
Try
conn.Open()
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
If dr.HasRows() Then
With Me.dtlDados
.DataSource = dr
.DataBind()
End With
Me.lblPaginaCorrente.Text = paginaAtual
End If
Catch ex As Exception
Response.Write(ex.ToString)
Finally
If Not (dr Is Nothing) Then
dr.Close()
End If
Call Me.ControlePaginacao(cmd.Parameters("@TotalClientes").Value)
End Try
End Sub

Código 4 - Procedimento CarregaGrid().


Analisando o código acima, criamos os parâmetros referente à quantidade de registros por página e a página requerida pelo Usuário e também definimos o parâmetro de OUTPUT chamado @TotalClientes, qual retorna a quantidade total de registros. Depois disso, verificamos se é retornado algum registro através da propriedade HasRows(), que retorna True, caso encontrado algum registro, e com isso populamos o DataList com este DataReader, e depois de fechado o DataReader, recuperamos o valor do parâmetro OUTPUT e invocamos o procedimento ControlePaginacao, que é responsável por exibir a quantidade de páginas e a página atual para o Usuário.

O procedimento ControlePaginacao recebe como parâmetro a quantidade total de Clientes, e baseado nisso, exibe a quantidade total de páginas e a página atual em que o Usuário se encontra:

1
2
3
4
5
6
7
Private Sub ControlePaginacao(ByVal totalClientes As Integer)
If (totalClientes Mod Me._totalRegistrosPagina) = 0 Then
Me.lblTotal.Text = totalClientes / Me._totalRegistrosPagina
Else
Me.lblTotal.Text = Convert.ToInt32((totalClientes / Me._totalRegistrosPagina) + 1)
End If
End Sub

Código 5 - Procedimento ControlePaginacao.


Agora, no evento Page_Load do WebForm, devemos chamar o procedimento CarregaGrid, passando como parâmetro o número 1, que refere-se a primeira página, lembrando que devemos chamar este procedimento quando não for PostBack. O código abaixo ilustra isso:

1
2
3
4
5
6
Private Sub Page_Load(...) Handles MyBase.Load
If Not Page.IsPostBack Then
Call Me.CarregaGrid(1)
Call Me.Navegacao(1)
End If
End Sub

Código 6 - Evento Page_Load do WebForm.

Ainda temos mais um procedimento para criar, chamado Navegacao, procedimento qual se encarrega de habilitar/desabilitar os botões de navegação da paginação. Este procedimento também é baseado na página atual que está sendo exibida. Vejamos abaixo o código deste procedimento na íntegra:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Private Sub Navegacao(ByVal paginaAtual As Integer)
If paginaAtual = 1 Then
With Me
    .lnkAnterior.Enabled = False
    .lnkProxima.Enabled = True
    .lnkPrimeiraPagina.Enabled = False
    .lnkUltimaPagina.Enabled = True
End With
ElseIf paginaAtual = Me.lblTotal.Text AndAlso paginaAtual> 1 Then
With Me
    .lnkAnterior.Enabled = True
    .lnkProxima.Enabled = False
    .lnkPrimeiraPagina.Enabled = True
    .lnkUltimaPagina.Enabled = False
End With
Else
With Me
    .lnkAnterior.Enabled = True
    .lnkProxima.Enabled = True
    .lnkPrimeiraPagina.Enabled = True
    .lnkUltimaPagina.Enabled = True
End With
End If
End Sub

Código 7 - Procedimento Navegacao.


Depois disso, nosso controle ficará semelhante à imagem abaixo:

Figura 1 - Controle DataList já com os controles de Navegação pelas páginas.

Mas ainda resta codificarmos os eventos Click() dos LinkButton responsáveis pela navegação das páginas. Temos quatro LinkButtons com as seguintes funcionalidades:

  • - Primeira página.
  • - Página anterior.
  • >> - Próxima página.
  • > - Última página.
1
2
3
4
Private Sub lnkPrimeiraPagina_Click(...) Handles lnkPrimeiraPagina.Click
Call Me.CarregaGrid(1)
Call Me.Navegacao(1)
End Sub

Código 8 - Procedimento que retorna à primeira página.


1
2
3
4
Private Sub lnkUltimaPagina_Click(...) Handles lnkUltimaPagina.Click
Call Me.CarregaGrid(Convert.ToInt32(Me.lblTotal.Text))
Call Me.Navegacao(Convert.ToInt32(Me.lblTotal.Text))
End Sub

Código 9 - Procedimento que vai para a última página.


1
2
3
4
5
Private Sub lnkAnterior_Click(...) Handles lnkAnterior.Click
Dim paginaAtual As Integer = Integer.Parse(Me.lblPaginaCorrente.Text.ToString()) - 1
Call Me.CarregaGrid(Convert.ToInt32(paginaAtual)
Call Me.Navegacao(Convert.ToInt32(paginaAtual)
End Sub

Código 10 - Procedimento que retorna à página anterior.


1
2
3
4
5
Private Sub lnkProxima_Click(...) Handles lnkProxima.Click
Dim paginaAtual As Integer = Integer.Parse(Me.lblPaginaCorrente.Text.ToString()) + 1
Call Me.CarregaGrid(Convert.ToInt32(paginaAtual)
Call Me.Navegacao(Convert.ToInt32(paginaAtual)
End Sub

Código 11 - Procedimento vai para a página posterior.


CONCLUSÃO: Vimos neste artigo como resgatar os dados da base de dados através de uma Stored Procedure, mas limitando os dados da página a ser exibida para proporcionar performance, retornando os dados para uma aplicação ASP.NET, e populando um controle DataList, controlando e exibindo a paginação.

Clique aqui para fazer o download do código.

Artigo desenvolvido utilizando:

* Visual Studio .NET 2003
* SQL Server 2000
* .NET Framework 1.1
* Windows XP Professional

Israel Aéce

Israel Aéce - Especialista em tecnologias de desenvolvimento Microsoft, atua como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. Como instrutor Microsoft, leciona sobre o desenvolvimento de aplicações .NET. É palestrante em diversos eventos Microsoft no Brasil e autor de diversos artigos que podem ser lidos a partir de seu site http://www.israelaece.com/. Possui as seguintes credenciais: MVP (Connected System Developer), MCP, MCAD, MCTS (Web, Windows, Distributed, ASP.NET 3.5, ADO.NET 3.5, Windows Forms 3.5 e WCF), MCPD (Web, Windows, Enterprise, ASP.NET 3.5 e Windows 3.5) e MCT.