Para quase todas as aplicações web de apresentação de dados, organizar a forma como os dados são exibidos e evitar sentimentos confusos para os usuários é um dos principais objetivos. Exibir 20 registros por página é certamente aceitável, mas exibir 10.000 registros por página pode facilmente causar transtornos aos usuários. Dividir os dados em múltiplas páginas para exibição, ou seja, paginar os dados, é a forma mais comum de resolver tais problemas.
1. Visão geral
O próprio ASP.NET fornece apenas um controle que suporta paginação de dados, ou seja, o controle de paginação DataGrid. No entanto, é mais adequado para uso em ambientes Intranet. Para ambientes de Internet, as funções fornecidas pelo controle de paginação DataGrid não parecem. será suficiente para construir uma aplicação Web flexível. Um dos motivos é que o controle DataGrid impõe restrições sobre onde os web designers podem colocar controles de paginação e sobre a aparência dos controles de paginação. Por exemplo, o controle DataGrid não permite o posicionamento vertical de controles de paginação. Outro controle que pode aproveitar a tecnologia de paginação é o Repetidor. Os desenvolvedores da Web podem usar o controle Repetidor para configurar rapidamente o método de exibição de dados, mas a função de paginação exige que os próprios desenvolvedores a implementem. As fontes de dados estão mudando constantemente e os métodos de apresentação de dados variam muito. Obviamente, seria uma perda de tempo personalizar os controles de paginação para essas condições de mudança. Construir um controle de paginação universal que não esteja limitado a controles de apresentação específicos ajudará muito a economizar tempo.
Um excelente controle universal de dados não apenas fornece funções regulares de paginação, mas também pode:
⑴ Fornece botões de navegação de paginação "Página inicial", "Página anterior", "Próxima página" e "Última página".
⑵ Ajuste seu status de acordo com a situação de exibição dos dados, ou seja, possui sensibilidade aos dados. Se o controle de paginação estiver configurado para exibir 10 registros por página, mas na verdade houver apenas 9 registros, o controle de paginação não deverá ser exibido quando os dados forem divididos em várias páginas para exibição, a "Home" e a "Top" de; a primeira página O botão "Página" não deve ser exibido, nem os botões "Próxima página" e "Última página" da última página.
⑶ Não é possível confiar em controles específicos de exibição de dados.
⑷ Capacidade de adaptação a diversas fontes de dados existentes e futuras.
⑸ O modo de exibição deve ser facilmente configurado e integrado em vários aplicativos.
⑹ Quando a paginação estiver pronta, lembre outros controles.
⑺ Mesmo web designers inexperientes devem ser capazes de usá-lo sem dificuldade.
⑻ Forneça dados de atributos sobre informações de paginação.
Atualmente existem alguns controles comerciais no mercado que fornecem as funções acima, mas são todos caros. Para muitos desenvolvedores, construir você mesmo um controle de paginação universal é a escolha ideal.
A Figura 1 mostra a interface de execução do controle de paginação universal neste artigo, no qual o controle usado para exibição é um controle Repetidor. O controle de paginação consiste em dois tipos de componentes: quatro botões de navegação e um conjunto de links de número de página.
Os usuários podem alterar facilmente os controles de exibição e a aparência do próprio controle de paginação. Por exemplo, o controle de exibição que coopera com o controle de paginação é substituído por um controle DataGrid, e o link do número da página e quatro botões de navegação são exibidos em duas linhas.
O ASP.NET oferece suporte a três maneiras de criar controles da Web personalizados: controles de usuário, controles compostos e controles personalizados. O nome do terceiro tipo de controle, o controle personalizado, é facilmente enganoso. Na verdade, todos os três controles devem ser considerados controles personalizados. A diferença entre os controles compostos e os chamados controles personalizados da Microsoft é que o primeiro requer o método CreateChildControls(). O método CreateChildControls() permite que o controle se redesenhe com base em determinados eventos. Para o pager universal deste artigo, usaremos um controle composto.
O diagrama de sequência UML a seguir descreve o mecanismo geral do controle de paginação universal.
Embora nosso objetivo seja tornar o controle de paginação universal independente do controle que representa os dados, é óbvio que deve haver alguma forma de o controle de paginação acessar os dados. Todo controle que herda da classe Control fornece um evento DataBinding. Registramos o próprio pager como ouvinte do evento DataBinding, para que o pager possa aprender sobre os dados e modificá-los. Como todos os controles que herdam da classe Control possuem esse evento DataBinding, o controle de pager atinge o objetivo de não depender de um controle de apresentação de dados específico - em outras palavras, o controle de pager pode ser vinculado a todos os controles que derivam da classe Control. Ou seja, pode ser vinculado a quase todos os controles da Web.
2. Funções principais
Quando o controle de apresentação aciona o evento DataBinding, o controle de paginação pode obter a propriedade DataSource. Infelizmente, a Microsoft não fornece interfaces que todas as classes de vinculação de dados implementem, como IdataSourceProvider, e nem todos os controles que herdam da classe Control ou WebControl possuem uma propriedade DataSource, portanto, não faz sentido fazer upcast para a classe Control, a única viável maneira O método é operar diretamente a propriedade DataSoruce por meio da API Reflection. Antes de discutir os métodos do manipulador de eventos, deve-se observar que para registrar um manipulador de eventos, você deve primeiro obter uma referência ao controle de apresentação. O controle de paginação expõe uma propriedade de string simples BindToControl:
string pública BindToControl
{
pegar
{
if (_bindcontrol == nulo)
throw new NullReferenceException("Antes de usar o controle de paginação, vincule-se a um controle definindo a propriedade BindToControl.");
retornar _bindcontrol;}
definir{_bindcontrol=valor;}
}
Este método é muito importante, portanto é melhor lançar uma mensagem mais clara em vez de lançar a NullReferenceException padrão. No método OnInit do controle de paginação, resolvemos a referência ao controle de apresentação. Este exemplo deve usar o manipulador de eventos OnInit (em vez do construtor) para garantir que a página aspx compilada JIT tenha BindToControl definido.
substituição protegida void OnInit (EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += novo EventHandler(BoundControl_DataBound);
base.OnInit(e);
...
}
A operação de pesquisa no controle de apresentação é concluída pesquisando o controle Pai do controle de paginação. Aqui, o Pai é a própria página. É perigoso usar Parent dessa maneira. Por exemplo, se o controle de paginação estiver incorporado em outro controle, como um controle Table, a referência Parent será, na verdade, uma referência ao controle Table. Como o método FindControl pesquisa apenas a coleção de controles atual, é impossível pesquisar, a menos que o controle de apresentação esteja na coleção. Uma abordagem mais segura é pesquisar recursivamente na coleção de controles até que o controle alvo seja encontrado.
Depois de encontrar o BoundControl, registramos o controle de paginação como ouvinte do evento DataBinding. Como o controle de paginação opera em uma fonte de dados, é importante que esse manipulador de eventos seja o último na cadeia de chamadas. No entanto, desde que o controle de apresentação registre o manipulador de eventos DataBinding no manipulador de eventos OnInit (o comportamento padrão), não haverá problema quando o controle de paginação operar a fonte de dados.
O manipulador de eventos DataBound é responsável por obter a propriedade DataSource do controle de apresentação.
private void BoundControl_DataBound (remetente do objeto, System.EventArgs e)
{
if (HasParentControlCalledDataBinding) retornar;
Digite tipo = sender.GetType();
_datasource = type.GetProperty("DataSource");
if (_datasource == nulo)
throw new NotSupportedException("O controle de paginação requer que o controle de apresentação contenha um DataSource.");
dados do objeto = _datasource.GetGetMethod().Invoke(sender,null);
_builder = Adaptadores[data.GetType()];
if (_builder == nulo)
throw new NullReferenceException("O adaptador apropriado não está instalado para manipular o seguinte tipo de fonte de dados: "+data.GetType());
_builder.Source = dados
;
BindParent();
RaiseEvent(DataUpdate,this);
}
No DataBound, tentamos obter a propriedade DataSource por meio da API Reflection e, em seguida, retornar uma referência à fonte de dados real. Agora que a fonte de dados é conhecida, o controle de paginação também deve saber como operar na fonte de dados. Para fazer com que o controle de paginação não dependa de um controle de apresentação específico, o problema é muito mais complicado. Entretanto, tornar o controle de paginação dependente de uma fonte de dados específica anula o objetivo de projetar um controle de paginação flexível. Precisamos usar uma arquitetura de plug-in para garantir que o controle de paginação possa lidar com várias fontes de dados, seja a fonte de dados fornecida pelo .NET ou uma fonte de dados personalizada.
Para fornecer uma arquitetura conectável robusta, escalável, construiremos uma solução usando o padrão [GoF] Builder.
Figura 4
A interface IDataSourceAdapter define os elementos mais básicos necessários para o controle de paginação operar os dados, o que equivale a um "plug".
interface pública IDataSourceAdapter
{
int TotalCount{obter;}
objeto GetPagedData(int início,int fim);
}
A propriedade TotalCount retorna o número total de elementos contidos na fonte de dados antes de processar os dados, enquanto o método GetPagedData retorna um subconjunto dos dados originais. Por exemplo: assumindo que a fonte de dados é uma matriz contendo 20 elementos, o controle de paginação é exibido. os dados como 10 elementos por página, então o subconjunto de elementos na primeira página será os elementos da matriz 0 a 9 e o subconjunto de elementos na segunda página será os elementos da matriz 10 a 19. DataViewAdapter fornece um plug do tipo DataView:
classe interna DataViewAdapter:IDataSourceAdapter
{
DataView _view privado;
DataViewAdapter interno (visualização DataView)
{
_visualizar = visualizar;
}
público int TotalCount
{
obter{retornar (_view == nulo) 0: _view.Table.Rows.Count;}
}
objeto público GetPagedData(int início, int fim)
{
Tabela DataTable = _view.Table.Clone();
for (int i = início;i<=fim && i<= TotalCount;i++)
{
tabela.ImportRow(_view[i-1].Row);
}
tabela de retorno;
}
}
DataViewAdapter implementa o método GetPagedData de IDataSourceAdapter, que clona o DataTable original e importa os dados do DataTable original para o novo DataTable. A visibilidade desta classe é intencionalmente definida como interna para ocultar detalhes de implementação dos desenvolvedores web e fornecer uma interface mais simples por meio da classe Builder.
classe abstrata pública AdapterBuilder
{
objeto privado _source;
privado void CheckForNull()
{
if (_source == null) throw new NullReferenceException("Uma fonte de dados legal deve ser fornecida");
}
objeto virtual público Fonte
{
pegar
{
CheckForNull();
return _fonte;}
definir
{
_fonte = valor;
CheckForNull();
}
}
adaptador público abstrato IDataSourceAdapter{get;}
}
A classe abstrata AdapterBuilder fornece uma interface mais gerenciável para o tipo IdataSourceAdapter. Devido ao maior nível de abstração, não precisamos mais usar IdataSourceAdapter diretamente. Além disso, este Construtor também torna a classe de implementação real, como DataViewAdapter, transparente para os usuários do controle de paginação:
public class DataTableAdapterBuilder:AdapterBuilder
{
privado DataViewAdapter _adapter;
privado DataViewAdapter ViewAdapter
{
pegar
{
if (_adapter == nulo)
{
Tabela DataTable = (DataTable)Fonte;
_adapter = novo DataViewAdapter(tabela.DefaultView);
}
retornar _adaptador;
}
}
substituição pública do adaptador IDataSourceAdapter
{
pegar
{
retornar ViewAdapter;
}
}
}
classe pública DataViewAdapterBuilder:AdapterBuilder
{
privado DataViewAdapter _adapter;
privado DataViewAdapter ViewAdapter
{
pegar
{ // Instanciação atrasada
if (_adapter == nulo)
{
_adapter = new DataViewAdapter((DataView)Fonte);
}
retornar _adaptador;
}
}
substituição pública do adaptador IDataSourceAdapter
{
obter{retornar ViewAdapter;}
}
}
O tipo DataView e o tipo DataTable estão tão intimamente relacionados que pode fazer sentido construir um DataAdapter genérico. Na verdade, basta adicionar outro construtor que manipule o DataTable. Infelizmente, quando os usuários precisam de funcionalidades diferentes para lidar com um DataTable, toda a classe deve ser substituída ou herdada. Se construirmos um novo Builder que utilize o mesmo IdataSourceAdapter, o usuário terá mais liberdade na escolha de como implementar o adaptador.
No controle de paginação, a operação de localização da classe Builder apropriada é concluída por uma coleção de tipo seguro.
classe pública AdapterCollection:DictionaryBase
{
string privada GetKey (chave de tipo)
{
retornar chave.NomeCompleto;
}
public AdapterCollection() {}
publicvoid Add (chave de tipo, valor do AdapterBuilder)
{
Dicionário.Add(GetKey(chave),valor);
}
publicbool contém (chave de tipo)
{
retornar Dicionário.Contains(GetKey(chave));
}
publicvoid Remover (chave de tipo)
{
Dicionário.Remove(GetKey(chave));
}
público AdapterBuilder isto[digite chave]
{
get{return (AdapterBuilder)Dicionário[GetKey(chave)];}
set{Dicionário[GetKey(chave)]=valor;}
}
}
AdapterCollection depende do tipo DataSource, e DataSource é introduzido de forma inteligente por meio de BoundControl_DataBound. A chave de índice usada aqui é o método Type.FullName, que garante a exclusividade da chave de índice de cada tipo. Ao mesmo tempo, também atribui ao AdapterCollection a responsabilidade de garantir que haja apenas um Builder para cada tipo. Adicione a pesquisa do Builder ao método BoundControl_DataBound e os resultados serão os seguintes:
public AdapterCollection Adaptadores
{
obter{retornar _adaptadores;}
}
private bool HasParentControlCalledDataBinding
{
obter{retornar _builder! = null;}
}
private void BoundControl_DataBound (remetente do objeto, System.EventArgs e)
{
if (HasParentControlCalledDataBinding) retornar;
Digite tipo = sender.GetType();
_datasource = type.GetProperty("DataSource");
if (_datasource == nulo)
throw new NotSupportedException("O controle de paginação requer que o controle de apresentação contenha um DataSource.");
dados do objeto = _datasource.GetGetMethod().Invoke(sender,null);
_builder = Adaptadores[data.GetType()];
if (_builder == nulo)
throw new NullReferenceException("O adaptador apropriado não está instalado para manipular o seguinte tipo de fonte de dados: "+data.GetType());
_builder.Source = dados
;
BindParent();
RaiseEvent(DataUpdate,this);
}
O método BoundControl_DataBound utiliza HasParentControlCalledDataBinding para verificar se o Construtor foi criado. Nesse caso, ele não executará mais a operação de localização do Construtor apropriado. A inicialização da tabela Adapters é feita no construtor:
public Pager()
{
SelectedPager=new System.Web.UI.WebControls.Style();
UnselectedPager = new System.Web.UI.WebControls.Style();
_adapters = new AdapterCollection();
_adapters.Add(typeof(DataTable),new DataTableAdapterBuilder());
_adapters.Add(typeof(DataView),new DataViewAdapterBuilder());
}
O último método a ser implementado é o BindParent, que é usado para processar e retornar dados.
privado vazio BindParent()
{
_datasource.GetSetMethod().Invoke(BoundControl,
novo objeto[]{_builder.Adapter.GetPagedData(StartRow,ResultsToShow*CurrentPage)});
}
Este método é muito simples, pois o processamento dos dados é realmente feito pelo Adaptador. Após a conclusão desse processo, usaremos a API Reflection novamente, mas desta vez para definir a propriedade DataSource do controle de apresentação.
3. Design de interface
Até agora, as funções principais do controle de paginação foram quase implementadas, mas se houver falta de métodos de apresentação apropriados, o controle de paginação não será muito útil.
Para separar efetivamente o método de apresentação da lógica do programa, a melhor maneira é usar templates ou, para ser mais específico, usar a interface Itemplate. Na verdade, a Microsoft entende claramente o poder dos modelos e os utiliza em quase todos os lugares, até mesmo no próprio analisador de páginas. Infelizmente, os modelos não são um conceito tão simples como algumas pessoas pensam, e leva algum tempo para compreender verdadeiramente a sua essência. Felizmente, há muita informação nesta área, por isso não entrarei em detalhes aqui. Voltando ao controle de paginação, ele possui quatro botões: página inicial, página anterior, próxima página, última página e, claro, o número de cada página. Os quatro botões de navegação são selecionados na classe ImageButton em vez da classe LinkButton. Do ponto de vista do design profissional da Web, os botões gráficos são obviamente mais úteis do que links monótonos.
public ImageButton FirstButton{obter {return primeiro;}}
public ImageButton LastButton{get {return Last;}}
public ImageButton PreviousButton{obter {return anterior;}}
public ImageButton NextButton{get {return Next;}}
Os números de página são construídos dinamicamente porque dependem do número de registros na fonte de dados e do número de registros exibidos em cada página. O número da página será adicionado a um Painel, e os web designers podem usar o Painel para especificar onde exibir o número da página. O processo de criação de números de página será discutido em detalhes posteriormente. Agora precisamos fornecer um modelo para o controle de paginação para que os usuários possam personalizar a aparência do controle de paginação.
[Contêiner de modelo (typeof (LayoutContainer))]
Layout público do ITemplate
{
obter{retorno (_layout;}
definir{_layout =valor;}
}
classe pública LayoutContainer:Control,INamingContainer
{
publicLayoutContainer()
{this.ID = "Página";}
}
A classe LayoutContainer fornece um contêiner para modelos. De modo geral, é sempre bom adicionar um ID personalizado ao contêiner do modelo, o que evitará problemas no tratamento de eventos e na realização de chamadas de página. O diagrama UML a seguir descreve o mecanismo de apresentação do controle de paginação.
Figura 5
A primeira etapa na criação de um modelo é definir o layout na página aspx:
<LAYOUT>
<asp:Panel id="Pager" Runat="servidor"></asp:Panel>
</LAYOUT>
Este exemplo de layout não contém nenhum elemento de formato, como tabelas, etc. É claro que aplicativos reais podem (e devem) adicionar elementos de formato, consulte mais instruções posteriormente.
A interface Itemplate fornece apenas um método InstantiateIn, que analisa o modelo e vincula o contêiner.
privado vazio InstantiateTemplate()
{
_container = novo LayoutContainer();
Layout.InstantiateIn(_container);
Primeiro = (ImageButton)_container.FindControl("Primeiro");
Anterior = (ImageButton)_container.FindControl("Anterior");
Próximo = (ImageButton)_container.FindControl("Próximo");
Último = (ImageButton)_container.FindControl("Último");
Holder = (Painel)_container.FindControl("Pager");
this.First.Click += new System.Web.UI.ImageClickEventHandler(this.First_Click);
this.Last.Click += new System.Web.UI.ImageClickEventHandler(this.Last_Click);
this.Next.Click += new System.Web.UI.ImageClickEventHandler(this.Next_Click);
this.Previous.Click += new System.Web.UI.ImageClickEventHandler(this.Previous_Click);
}
A primeira coisa que o método InstatiateTemplate do controle faz é instanciar o template, ou seja, chamar Layout.InstantiateIn(_container). Um contêiner é na verdade um tipo de controle e seu uso é semelhante a outros controles. O método InstantiateTemplate usa esse recurso para localizar os quatro botões de navegação e o Painel usado para armazenar o número da página. Os botões de navegação são encontrados por seus IDs. Esta é uma pequena restrição nos controles de paginação: os botões de navegação devem ter IDs especificados, que são Primeiro, Anterior, Próximo e Último. Além disso, o ID do Painel deve ser Pager, caso contrário não. encontrado. Infelizmente, esta parece ser a melhor abordagem para o mecanismo de apresentação que escolhemos, mas esperamos que, com a documentação adequada, esta pequena limitação não cause problemas; Outra alternativa é deixar cada botão herdar da classe ImageButton, definindo assim um novo tipo, como cada botão é de um tipo diferente, uma pesquisa recursiva pode ser implementada no container para encontrar vários botões específicos, eliminando a necessidade de utilizar o ID do botão; atributo.
Depois de encontrar os quatro botões, vincule os manipuladores de eventos apropriados a eles. Uma decisão importante deve ser tomada aqui, ou seja, quando chamar InstantiateTemplate. Geralmente, esse tipo de método deve ser chamado no método CreateChildControls, pois o objetivo principal do método CreateChildControls é esse tipo de tarefa de criação de controles filhos. Como o controle de paginação nunca modifica seus controles filho, ele não precisa da funcionalidade fornecida por CreateChildControls para modificar o estado de exibição com base em algum evento. Quanto mais rápido o controle filho for exibido, melhor, então o local ideal para chamar o método InstantiateTemplate é no evento OnInit.
substituição protegida void OnInit (EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += novo EventHandler(BoundControl_DataBound);
InstantiateTemplate();
Controls.Add(_container);
base.OnInit(e);
}
Além de chamar o método InstantiateTemplate, o método OnInit também tem outra tarefa importante de adicionar o contêiner ao controle de paginação. Se você não adicionar o contêiner à coleção de controles do pager, o modelo não será exibido porque o método Render nunca será chamado.
Os templates também podem ser definidos programaticamente através da implementação da interface Itemplate. Além de ser uma medida para aumentar a flexibilidade, esse recurso também pode fornecer um template padrão para uso quando o usuário não fornecer um template através de uma página aspx.
classe pública DefaultPagerLayout:ITemplate
{
ImageButton privado Próximo;
ImageButton privado primeiro;
ImageButton privado Último;
ImageButton privado Anterior;
Pager do painel privado;
public DefaultPagerLayout()
{
Próximo = new ImageButton();
Primeiro = new ImageButton();
Último = new ImageButton();
Anterior = new ImageButton();
Pager = new Panel();
Next.ID="Próximo"; Next.AlternateText="Próxima página";Next.ImageUrl="play2.gif";
First.ID="Primeiro"; First.AlternateText="Home";First.ImageUrl="play2L_dis.gif";
Last.ID = "Última"; Last.AlternateText = "Última página";
Anterior.ID="Anterior"; Anterior.AlternateText="Página anterior";Previous.ImageUrl="play2L.gif";
Pager.ID="Pager";
}
public void InstantiateIn (controle de controle)
{
control.Controls.Clear();
Tabela tabela = new Tabela();
tabela.BorderWidth = Unit.Pixel(0);
tabela.CellSpacing= 1;
tabela.CellPadding =0;
linha TableRow = new TableRow();
linha.VerticalAlign = VerticalAlign.Top;
tabela.Rows.Add(linha);
Célula TableCell = new TableCell();
célula.HorizontalAlign = HorizontalAlign.Right;
célula.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add(Primeiro);
cell.Controls.Add(Anterior);
linha.Células.Add(célula);
célula = new TableCell();
cell.HorizontalAlign= HorizontalAlign.Center;
cell.Controls.Add(Pager);
linha.Células.Add(célula);
célula = new TableCell();
célula.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add(Próximo);
cell.Controls.Add(Último);
linha.Cells.Add (célula)
;
}
}
DefaultPagerLayout fornece todos os elementos de navegação programaticamente e os adiciona à página aspx, mas desta vez os elementos de navegação são formatados com tabelas HTML padrão. Agora, caso o usuário não forneça um modelo de apresentação, o programa fornecerá automaticamente um modelo padrão.
[TemplateContainer(typeof(LayoutContainer))]
Layout público do ITemplate
{
obter{retornar (_layout == nulo)? novo DefaultPagerLayout():_layout;}
definir{_layout =valor;}
}
Vamos dar uma olhada no processo de geração de cada número de página. O controle de paginação primeiro precisa determinar alguns valores de propriedade e usar esses valores de propriedade para determinar quantos números de páginas diferentes devem ser gerados.
public int Página Atual
{
pegar
{
string cur = (string)ViewState["CurrentPage"];
return (cur == string.Empty || cur ==null)? 1: int.Parse(cur);
}
definir
{
ViewState["CurrentPage"] = valor.ToString();}
}
public int PagersToShow
{
obter{retornar _resultados;}
definir{_resultados = valor;}
}
public int ResultsToShow
{
obter{return _resultsperpage;}
definir{_resultadosperpage = valor;}
}
CurrentPage na verdade salva a página atual no ViewState do número da página. As propriedades definidas pelo método PagersToShow permitem ao usuário especificar quantas páginas exibir, enquanto as propriedades definidas por ResultsToShow permitem ao usuário especificar em quantos registros exibir. cada página. O valor padrão é 10.
NumberofPagersToGenerate retorna o número atual de números de páginas que devem ser gerados.
privado int PagerSequence
{
pegar
{
retornar Convert.ToInt32
(Math.Ceiling((duplo)CurrentPage/(duplo)PagersToShow));}
}
private int NumberOfPagersToGenerate
{
obter{return PagerSequence*PagersToShow;}
}
private int TotalPagesToShow
{
get{return Convert.ToInt32(Math.Ceiling((double)TotalResults/(double)_resultsperpage));}
}
público int TotalResults
{
obter{return _builder.Adapter.TotalCount;}
}
O método TotalPagesToShow retorna o número total de páginas a serem exibidas, ajustado pela propriedade ResultsToShow predefinida do usuário.
Embora o ASP.NET defina alguns estilos padrão, eles podem não ser muito práticos para usuários de controles de paginação. Os usuários podem ajustar a aparência do controle de paginação por meio de estilos personalizados.
estilo público UnSelectedPagerStyle {get {return UnselectedPager;}}
public Style SelectedPagerStyle {get {return SelectedPager;}}
UnSelectedPagerStyle fornece o estilo usado quando o número da página não é selecionado e SelectedPagerStyle fornece o estilo usado quando o número da página é selecionado.
void privado GeneratePagers (controle WebControl)
{
control.Controls.Clear();
int pager = (PagerSequence-1)* PagersToShow +1
para (;pager<=NumberOfPagersToGenerate && pager<=TotalPagesToShow;pager++)
{
LinkButton link = new LinkButton();
link.Text = pager.ToString();
link.ID = pager.ToString();
link.Click += new EventHandler(this.Pager_Click);
if (link.ID.Equals(CurrentPage.ToString()))
link.MergeStyle(SelectedPagerStyle);
outro
link.MergeStyle(UnSelectedPagerStyle);
control.Controls.Add(link);
control.Controls.Add(new LiteralControl(" "));
}
}
privado void GeneratePagers()
{
GeneratePagers(Suporte);
}
O método GeneratePagers cria dinamicamente todos os números de página, que são botões do tipo LinkButton. Os atributos de rótulo e ID de cada número de página são atribuídos por meio de um loop e, ao mesmo tempo, o evento click é vinculado ao manipulador de eventos apropriado. Finalmente, o número da página é adicionado a um controle contêiner – neste caso, um objeto Panel. O ID do botão serve para identificar qual botão acionou o evento de clique. A seguir está a definição do manipulador de eventos:
private void Pager_Click(object sender, System.EventArgs e)
{
Botão LinkButton = (LinkButton) remetente;
PáginaAtual = int.Parse(botão.ID);
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Pager));
Atualizar();
}
private void Next_Click (remetente do objeto, System.Web.UI.ImageClickEventArgs e)
{
if (CurrentPage<TotalPagesToShow)
PáginaAtual++;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Next));
Atualizar();
}
private void Previous_Click (remetente do objeto, System.Web.UI.ImageClickEventArgs e)
{
if (Página Atual> 1)
Página Atual--;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Previous));
Atualizar();
}
private void First_Click (remetente do objeto, System.Web.UI.ImageClickEventArgs e)
{
PáginaAtual = 1;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.First));
Atualizar();
}
private void Last_Click (remetente do objeto, System.Web.UI.ImageClickEventArgs e)
{
CurrentPage = TotalPagesToShow;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Last));
Atualizar();
}
Esses manipuladores de eventos são semelhantes porque primeiro alteram a página atual do controle de paginação e depois atualizam o controle vinculado.
atualização nula privada()
{
if (!HasParentControlCalledDataBinding) retornar;
ApplyDataSensitivityRules();
BindParent();
BoundControl.DataBind();
}
Primeiro, o controle de paginação verifica se os adaptadores necessários foram inicializados chamando o método HasParentControlCalledDataBinding. Nesse caso, aplique as regras apontadas anteriormente para ajustar automaticamente os controles com base nas condições de exibição de dados ao controle atual. Essas regras fazem com que o controle de paginação se comporte de maneira diferente com base nas diferentes condições dos dados no BoundControl. Embora essas regras sejam controladas internamente pelo controle de paginação, elas podem ser facilmente removidas do controle usando o modo Estado [GoF] quando necessário.
bool público IsDataSensitive
{
obter{return _isdatasensitive;}
definir{_isdatasensitive = valor;}
}
bool privado IsPagerVisible
{
get{return (TotalPagesToShow != 1) && IsDataSensitive;}
}
bool privado IsPreviousVisible
{
pegar
{
retornar (!IsDataSensitive)?
(Página Atual! = 1);
}
}
bool privado IsNextVisible
{
pegar
{
retornar (!IsDataSensitive)?
(Página Atual! = TotalPagesToShow);
}
}
private void ApplyDataSensitivityRules()
{
FirstButton.Visible = IsPreviousVisible;
PreviousButton.Visible = IsPreviousVisible;
LastButton.Visible = IsNextVisible;
NextButton.Visible = IsNextVisible;
if (IsPagerVisible) GeneratePagers();
}
O método ApplyDataSensitivityRules implementa regras predefinidas como IsPagerVisible, IsPreviousVisible e IsNextVisible. Por padrão, o controle de paginação terá essas regras habilitadas, mas o usuário poderá desativá-las configurando a propriedade IsDataSensitive.
Até agora, a parte de exibição do controle de paginação foi basicamente projetada. O último trabalho de acabamento restante é fornecer vários manipuladores de eventos para que os usuários possam fazer os ajustes necessários quando ocorrerem vários eventos de controle de paginação.
delegado público void PageDelegate (remetente do objeto, PageChangedEventArgs e);
public enum PagedEventInvoker {Próximo, Anterior, Primeiro, Último, Pager}
classe pública PageChangedEventArgs: EventArgs
{
private int nova página;
invocador Enum privado;
public PageChangedEventArgs(int newpage):base()
{
esta.novapágina = novapágina;
}
public PageChangedEventArgs(int newpage,invocador PagedEventInvoker)
{
esta.novapágina = novapágina;
this.invoker = invocador;
}
public int NewPage {get{return newpage;}}
public Enum EventInvoker{get{return invocador;}}
}
Como o controle de paginação precisa retornar parâmetros de evento personalizados, definimos uma classe PageChangedEventArgs dedicada. A classe PageChangedEventArgs retorna o tipo PagedEventInvoker, que é um enumerador dos controles que podem acionar o evento. Para lidar com parâmetros de eventos personalizados, definimos um novo delegado, PageDelegate. O evento é definido da seguinte forma:
public event PageDelegate PageChanged;
evento público EventHandler DataUpdate
Quando um evento não possui um ouvinte de evento correspondente, o ASP.NET lançará uma exceção. O controle de paginação define os seguintes métodos RaiseEvent.
private void RaiseEvent(EventHandler e,objeto remetente)
{
this.RaiseEvent(e,this,nulo);
}
private void RaiseEvent(EventHandler e,object sender, PageChangedEventArgs args)
{
se(e!=nulo)
{
e(remetente,argumentos);
}
}
private void RaiseEvent(PageDelegate e,objeto remetente)
{
this.RaiseEvent(e,this,nulo);
}
private void RaiseEvent(PageDelegate e,objeto remetente, PageChangedEventArgs args)
{
se(e!=nulo)
{
e(remetente,argumentos);
}
}
Os manipuladores de eventos agora podem acionar eventos chamando métodos RaiseEvent individuais.
4. Exemplos de aplicação
neste momento, o design do controle de paginação foi concluído e pode ser usado oficialmente. Para usar o controle de paginação, basta ligá -lo a um controle de apresentação.
<ItemTemplate>
Coluna 1:
<%# Convert.toString (databinder.eval (container.dataitem, "column1"))%>
<br>
Coluna 2:
<%# Convert.toString (databinder.eval (container.dataitem, "column2"))%>
<br>
Coluna 3:
<%# Convert.toString (databinder.eval (container.dataitem, "column3"))%>
<br>
</ItemTemplate>
</asp: repetidor >
< CC1: Pager ID = "Pager" ResultShow = "2" Runat = "Server" BindTocontrol = "Repeater" >
< SelectedPagersTyle backcolor = "Amarelo" />
</cc1: pager >
A página ASPX acima liga o controle de paginação a um controle de repetidor, define o número de registros exibidos em cada página para 2, a cor do número da página selecionada é amarelo e usa o layout padrão como mostrado na Figura 1. Abaixo está outro exemplo, que vincula o controle de paginação a um datagrid, como mostrado na Figura 2.
< ASP: DATAGRID ID = "GRID" RUNAT = "Server" ></Asp: Datagrid >
< CC1: Pager id = "PagerGrid" ResultShow = "2" Runat = "Server" BindTocontrol = "Grid" >
< SelectedPagersTyle backcolor = "Red" ></SelectedPagersTyle >
< Layout >
< Asp: ImageButton ID = "First" Runat = "Server" imageurl = "play2l_dis.gif" alternateText = "home" ></asp: ImageButton >
< ASP: ImageButton ID = "anterior" runat = "server" imageurl = "play2l.gif" alternateText = "Página anterior" ></asp: ImageButton >
< Asp: ImageButton ID = "Next" Runat = "Server" Imageurl = "play2.gif" alternateText = "Próxima página" ></asp: ImageButton >
< ASP: ImageButton ID = "Last" Runat = "Server" Imageurl = "Play2_Dis.gif" alternateText = "Last Page" ></Asp: ImageButton >
< ASP: Painel ID = "Pager" Runat = "Server" ></ASP: Painel >
</layout >
</cc1: pager >
O teste mostra que o controle de paginação não depende de controles de apresentação específicos. o exemplo completo.
Embora aprender a desenvolver controles da Web personalizados não seja uma tarefa fácil, os benefícios de dominar essa habilidade são evidentes. Times.
http://www.cnblogs.com/niit007/archive/2006/08/13/475501.html