Custom Paging ASP.NET com Repeater e Data List

Introdução

Este artigo mostra como fazer um paging para os controles ASP.NET DataList e Repeater que não possuem suporte nativo ao paging.Essa é uma das maneiras mais eficientes que eu conheço de criar custom paging porque não requer uso de ViewState em  nenhum controle utilizado, e além de tudo o SEO é amigável a buscadores como google e bing que podem “rastrear” todos os dados da página facilmente.

customSEO

Como criar o custom paging

Para explicar o funcionamento do paging,eu utilizei o Sql Server 2005 ,o banco Northwind utilizando a tabela “Products”.

ASPX Page – Repeater Control

Vamos ver o código para o controle repeater:

<asp:Repeater ID=”Repeater1″ runat=”server” EnableViewState=”false”>

<ItemTemplate>

<b><%# Eval(“ProductName”) %></b> <%# Eval(“QuantityPerUnit”) %><br />

</ItemTemplate>

</asp:Repeater>

<asp:Literal ID=”Literal1″ runat=”server” EnableViewState=”false”></asp:Literal>

O item template do repeater contém dados das colunas “ProductName” e “QuantityPerUnit” da tabela Products.A última linha é controle Literal que irá listar os links numéricos para as diferentes páginas.Note que os controles possuem a propriedade “enableviewstate=false “ para assegurar que não se usa nenhum viewstate na página para armazenar valores,e isso faz com que nossa página se torne mais veloz.

O código da página.


using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Data.SqlClient;

using System.Data;

using System.Text;

using System.Web.Services;

public partial class Paging_Paging : System.Web.UI.Page

{

string connection = @”Server=xxxx\sqlexpress; Initial Catalog=Northwind; User; Password=xxxxxx”;

int startIndex = 0;

int thisPage = 1;

int pageSize = 10;

int totalRowNumber = 0;

protected void Page_Load(object sender, EventArgs e)

{

if (!Page.IsPostBack)

{

if (Request[“start”] != null && Request[“page”] != null)

{

int.TryParse(Request[“start”].ToString(), out startIndex);

int.TryParse(Request[“page”].ToString(), out thisPage);

}

}

BindGrid();

}

protected void BindGrid()

{

DataTable dt = new DataTable();

using(SqlConnection cn =new SqlConnection(connection))

{

using(SqlCommand cmd=new SqlCommand(“LoadDados”,cn))

{

cmd.CommandType = CommandType.StoredProcedure;

SqlParameter[] parametros = new SqlParameter[3];

parametros[0] = new SqlParameter(“@startRowIndex”, SqlDbType.Int);

parametros[0].Value = startIndex;

parametros[1] = new SqlParameter(“@pageSize”, SqlDbType.Int);

parametros[1].Value = pageSize;

parametros[2] = new SqlParameter(“@totalCount”, SqlDbType.Int);

parametros[2].Direction = ParameterDirection.Output;

cmd.Parameters.AddRange(parametros);

using (SqlDataAdapter da = new SqlDataAdapter(cmd))

{

cn.Open();

da.Fill(dt);

cn.Close();

}

totalRowNumber = int.Parse(parametros[2].Value.ToString());

}

}

Repeater1.DataSource = dt;

Repeater1.DataBind();

Literal1.Text = DoPaging(thisPage, totalRowNumber, pageSize, “Paging.aspx”, “”);

}

protected string DoPaging(int pageNum, int total, int pageTam, string pageName, string extraDado)

{

int pagNo = 0;

int start = 0;

int loop = total / pageTam;

int remainder = total % pageTam;

StringBuilder sb = new StringBuilder(“<br /><b><font color=\”green\”>Page:</font> “,500);

for (int i = 0; i < loop; i++)

{

pagNo = i + 1;

if (pagNo.Equals(pageNum))

sb.Append(pagNo + “&nbsp;|”);

else

sb.Append(“<a href=\”” + pageName + “?start=” + start + “&page=” + pagNo + extraDado + “\” title=\”Go toPage “ + pagNo + “\”>” + pagNo + “</a> | “);

start += pageTam;

}

if (remainder > 0)

{

pagNo++;

if (pagNo.Equals(pageNum))

{

sb.Append(“<b>” + pagNo + “&nbsp;</b>| “);

}

else

{

sb.Append(“<a href=\”” + pageName + “?start=” + start + “&page=” + pagNo + extraDado + “\” title=\”Go to page “ + pagNo + “\”>” + pagNo + “</a> | “);

}

}

return sb.ToString() + “</b></span>” ;

}

}

No código acima eu tenho 4 variaveis relacionadas com o custom paging,são elas:

1-startIndex – serve para armazenar o índice inicial entre as linhas que serão retornadas da consulta,isso nos ajudará a buscar dados para a próxima página.

2-thisPage – é utilizada para armazenar o valor da página atual.

3-pageSize – para armazenar o tamanho da página (nº de registros mostrados por página)

4-totalRowNumber – armazena o nº total de linha no banco,serve para calcular o número total de páginas de nosso paging.

Primeiramente,vamos dar uma olhada no método BindGrid().Esse método contém,um código simples de acesso ao Sql Server  utilizando a Stored Procedure “LoadDados”com dois parâmetros input, (startRowIndex e pageSize) e um parametro de saída (totalCount).O código para a stored procedure “LoadDados”segue abaixo:

set ANSI_NULLS ON

set QUOTED_IDENTIFIER ON

GO

ALTER PROCEDURE [dbo].[LoadDados]

@startRowIndex int,

@pageSize int,

@totalCount int output

AS

BEGIN

SET NOCOUNT ON;

SET @totalCount = 0

SET @startRowIndex = @startRowIndex + 1;

Select * from(Select ProductName, QuantityPerUnit, Row_Number() over(order by ProductName asc)as linhas from Products where CategoryID = 1) as lista where linhas between @startRowIndex and (@startRowIndex + @pageSize) – 1  order by ProductName

Select @totalCount = count(ProductName) from Products where CategoryID = 1

END

O primeiro comando SELECT  está buscando dados paginados do banco e o segundo SELECT está buscando o total de linhas do banco.Note que o 1º SELECT só é suportado no SQL SERVER 2005 .

Depois de exceutar a Stored Procedure no método BindGrid() , eu estou dando um “DataBind()” no repeater com o DataTable que contém os dados retornados do banco.

A última linha desse método é pra escrever o número de páginas e os respectivos links pra cada uma delas.

Esse links estão sendo criados no métodoDoPaging() que pega 5 parâmetoros,são eles:

1-pageNum – esse é o valor da página atual sendo mostrada.

2-total – esse é o total de registros retornado do último SELECT da stored procedure “LoadDados”

3-pageTam – esse é o tamanho da página (total de registros mostrados por vez em cada página)

4-pageName – esse é o nome da página onde o paging está sendo criado (página atual).É uma boa prática manter essa função numa classe comum porque assim você poderá acessar essa função de paging em diferentes páginas no seu projeto,nesse caso esse parâmetro será útil.

5-extraDado – essa é uma string adicional que você queira adicionar com os links de suas páginas (No mundo real você pode querer passar seus parâmetros de filtro ou Ids nos links) Eu não utilizei nesse exemplo por isso deixei vazio.

O código dentro do método DoPaging() não é muito complicado,é matemática:divide-se o total de registros pelo número de registros em cada página,, faz se um loop pelo resultado (dividendo) e escreve os links numéricos.Se tiver um resto, escreve o último paging link também.

Uma vez pronto, teste seu projeto e você deveráver um Custom SEO paging  amigável dos registros como mostrado na figura acima.Como enableViewState dos controles estão false,não é preciso se preocupar com o tamanho do viewstate de cada página.

Esses links são HyperLinks com valores queryString por isso motores de busca irão facilmente encontra-los.

Esse post descreveu como alcançar um Veloz Custom SEO paging com repeater  ou DataList .Esta técnica também pode ser usada com outros controles como GridView etc…


Anúncios

Validações – A maneira fácil de produzir regras de negócio na sua UI

Validadores tornam fácil  a tarefa de validar dados na UI,mas você sabia como é fácil criar seu próprio controle validador?


Validators são controles excelentes,mas os que vem com ASP.NET são , digamos,limitados.Para a maioria da lógica de negócio,desenvolvedores usam ou CustomValidators (que permite você inserir seu próprio código de validação),ou criam suas próprias validações do inicio ao fim.A resposta correta é geralmente criar seu próprio Validador para chamar o código de validação na sua aplicação.

Eu tinha um cliente que deveria coletar informações de endereço de diversas páginas. Adicionar todos os controles necessários e códigos para essa páginas era realmente entediante,eu considerei então criar um “Endereço user control”, que eu poderia arrastar para qualquer página.Porém,user controls só podem ser usados em um projeto.
Ao invés disso então eu criei um novo Validator o “EnderecoValidator” e coloquei na
ToolBox,assim eu pude usar em outros projetos também.

Eu trouxe o enderecovalidator pra esse exemplo porque ele suporta validar múltiplos controles (não somente o controle enderecoRua,mas também os controles da cidade,do estado, e do país)que poderiam ser text Boxes  ou qualquer outro tipo de controle de lista(dropdown-list, radiobutton list).

Criando o controle

Criar um controle de validação é relativamente simples:Crie um novo projeto,e na aba Web,escolha ASP.NET server control(eu dei o nome de “PHValidator”).Com o projeto criado mude o nome da classe ServerControl1.cs para o nome que você que dar para o controle(eu usei enderecoValidator), a classe herdará da classe BaseValidator.Você também precisa mudar o nome,do controle no atributo ToolBoxData que aparece na declaração da classe.

using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace PHVValidators
{
[DefaultProperty(“ControlToValidate”)]
[ToolboxData(“<{0}:EnderecoValidator runat=server>
</{0}:EnderecoValidator>”)]
public class AddressValidator :
System.Web.UI.WebControls.BaseValidator
{

Tudo que você precisa adicionar para o baseValidator, é um teste para validar dados e qualquer propriedade adicional requirida para suportar seu código de validação.

Vamos ver primeiramente as propriedades.Para elas,eu preciso de alguns campos para armazenar os nomes dos controles que contém parte do endereço.Eu também preciso de algumas propriedades para capturar e armazenar os dados.Como essas propriedades devem receber o nome dos controles presentes em cada página,eu gostaria de ter a propriedade na janela de propriedades(property window),mostrada como uma drop-list de controles presentes na página.Isso é fácil de fazer:eu só adicionei o TypeConverterAttribute para a propriedade,passando o tipo de ValidadorControlConverter.Aqui um exemplo de propriedade para armazenar o nome do controle que tem o nome da cidade.

string cityId = “”;

[TypeConverterAttribute(typeof(ValidatedControlConverter))] _
string CityId
{
get
{
return cityId;
}
set
{
cityId = value;
}
}

Validando os dados

O único método da classe BaseValidator que você precisa reescrever,é o EvaluaterIsValid e retornar true ou false,dependendo do resultado de seu teste.Para pegar esses valores dos controles da páginas,chame o método,GetControlValidationValue passando o nome do controle que você quer.Esse código recupera o valor do controle cujo nome foi passado na propriedade CityId e retorna false para indicar que o teste falhou e o controle está vazio.

protected override bool EvaluateIsValid()
{
string CityValue = this.GetControlValidationValue(this.CityId);
if (CityValue == “”)
{
return false;
}
return true;
}

Na vida real,você provavelmente vai chamar métodos de validação na sua camada intermediária de negócios para checar seus dados.

Pra testar seu validator , adicione um projeto web para o projeto atual do seu validatorControl.Seu novo validador aparecerá no topo da toolbox – você pode arrasta-lo para a página ,mudar sua propriedades e debugar seu código.Se você fizer alguma mudança no seu ValidatorControl,você precisa dar um rebuild na sua solution,apagar o controle da tela,e arrasta-lo novamente.