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:
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
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.
|
|
|
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.
|
|
|
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.
|
|
|
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