Pour presque toutes les applications Web de présentation de données, organiser la manière dont les données sont affichées et éviter toute confusion chez les utilisateurs est l'un des principaux objectifs. Afficher 20 enregistrements par page est certes acceptable, mais afficher 10 000 enregistrements par page peut facilement causer des désagréments aux utilisateurs. Le fractionnement des données en plusieurs pages à afficher, c'est-à-dire la pagination des données, est le moyen le plus courant de résoudre de tels problèmes.
1. Présentation
ASP.NET lui-même ne fournit qu'un seul contrôle prenant en charge la pagination des données, à savoir le contrôle de pagination DataGrid. Cependant, il est plus adapté à une utilisation dans les environnements Intranet. Pour les environnements Internet, les fonctions fournies par le contrôle de pagination DataGrid ne semblent pas le faire. suffire à construire une application Web flexible. L'une des raisons est que le contrôle DataGrid impose des restrictions sur l'endroit où les concepteurs Web peuvent placer les contrôles de pagination et sur l'apparence des contrôles de pagination. Par exemple, le contrôle DataGrid n'autorise pas le placement vertical des contrôles de pagination. Un autre contrôle qui peut tirer parti de la technologie de pagination est Repeater. Les développeurs Web peuvent utiliser le contrôle Repeater pour configurer rapidement la méthode d'affichage des données, mais la fonction de pagination nécessite que les développeurs l'implémentent eux-mêmes. Les sources de données changent constamment et les méthodes de présentation des données varient considérablement. Ce serait évidemment une perte de temps de personnaliser les contrôles de pagination pour ces conditions changeantes. La construction d'un contrôle de pagination universel qui ne se limite pas à des contrôles de présentation spécifiques permettra de gagner beaucoup de temps.
Un excellent contrôle universel des données fournit non seulement des fonctions de pagination régulières, mais peut également :
⑴ Fournissez les boutons de navigation « Page d'accueil », « Page précédente », « Page suivante » et « Dernière page ».
⑵ Ajustez son statut en fonction de la situation d'affichage des données, c'est-à-dire qu'il est sensible aux données. Si le contrôle de pagination est configuré pour afficher 10 enregistrements par page, mais qu'il n'y a en réalité que 9 enregistrements, alors le contrôle de pagination ne doit pas être affiché lorsque les données sont divisées en plusieurs pages pour l'affichage, la « Accueil » et le « Haut » de ; la première page Le bouton « Page » ne doit pas être affiché, ni les boutons « Page suivante » et « Dernière page » de la dernière page.
⑶ Ne peut pas s'appuyer sur des contrôles d'affichage de données spécifiques.
⑷ Capacité à s'adapter aux diverses sources de données existantes et futures.
⑸ Le mode d'affichage doit être facilement configuré et facilement intégré dans diverses applications.
⑹ Lorsque la recherche est prête, rappelez les autres commandes.
⑺ Même les concepteurs de sites Web inexpérimentés devraient pouvoir l'utiliser sans difficulté.
⑻ Fournissez des données d'attribut sur les informations de pagination.
Il existe actuellement sur le marché certains contrôles commerciaux qui offrent les fonctions ci-dessus, mais ils sont tous coûteux. Pour de nombreux développeurs, construire soi-même un contrôle de pagination universel est le choix idéal.
La figure 1 montre l'interface en cours d'exécution du contrôle de pagination universel dans cet article, dans lequel le contrôle utilisé pour l'affichage est un contrôle Répéteur. Le contrôle de pagination se compose de deux types de composants : quatre boutons de navigation et un ensemble de liens de numéro de page.
Les utilisateurs peuvent facilement modifier les commandes d'affichage et modifier l'apparence du contrôle de pagination lui-même. Par exemple, le contrôle d'affichage qui coopère avec le contrôle de pagination est remplacé par un contrôle DataGrid, et le lien du numéro de page et quatre boutons de navigation sont affichés sur deux lignes.
ASP.NET prend en charge trois manières de créer des contrôles Web personnalisés : les contrôles utilisateur, les contrôles composites et les contrôles personnalisés. Le nom du troisième type de contrôle, le contrôle personnalisé, est facilement trompeur. En fait, les trois contrôles doivent être considérés comme des contrôles personnalisés. La différence entre les contrôles composites et les contrôles dits personnalisés de Microsoft est que les premiers nécessitent la méthode CreateChildControls(). La méthode CreateChildControls() permet au contrôle de se redessiner en fonction de certains événements. Pour le pager universel de cet article, nous utiliserons un contrôle composite.
Le diagramme de séquence UML suivant décrit le mécanisme général du contrôle de pagination universel.
Bien que notre objectif soit de rendre le contrôle de pagination universel indépendant du contrôle qui représente les données, il est évident qu'il doit y avoir un moyen pour le contrôle de pagination d'accéder aux données. Chaque contrôle qui hérite de la classe Control fournit un événement DataBinding. Nous enregistrons le pager lui-même en tant qu'écouteur de l'événement DataBinding, afin que le pager puisse en apprendre davantage sur les données et les modifier. Étant donné que tous les contrôles qui héritent de la classe Control ont cet événement DataBinding, le contrôle pager atteint l'objectif de ne pas s'appuyer sur un contrôle de présentation de données spécifique - en d'autres termes, le contrôle pager peut être lié à tous les contrôles qui dérivent de la classe Control. Autrement dit, il peut être lié à presque tous les contrôles Web.
2. Fonctions principales
Lorsque le contrôle de présentation déclenche l'événement DataBinding, le contrôle de pagination peut obtenir la propriété DataSource. Malheureusement, Microsoft ne fournit pas d'interfaces implémentées par toutes les classes de liaison de données, telles que IdataSourceProvider, et tous les contrôles qui héritent de la classe Control ou WebControl n'ont pas de propriété DataSource. Il est donc inutile de procéder à une conversion ascendante vers la classe Control, la seule solution possible. way La méthode consiste à exploiter directement la propriété DataSoruce via l'API Reflection. Avant de discuter des méthodes de gestion d'événements, il convient de noter que pour enregistrer un gestionnaire d'événements, vous devez d'abord obtenir une référence au contrôle de présentation. Le contrôle de pagination expose une simple propriété de chaîne BindToControl :
chaîne publique BindToControl
{
obtenir
{
si (_bindcontrol == null)
throw new NullReferenceException("Avant d'utiliser le contrôle de pagination, veuillez vous lier à un contrôle en définissant la propriété BindToControl.");
retourner _bindcontrol ;}
définir{_bindcontrol=valeur;}
}
Cette méthode est très importante, il est donc préférable de lancer un message plus clair au lieu de lancer l'exception NullReferenceException standard. Dans la méthode OnInit du contrôle de pagination, nous résolvons la référence au contrôle de présentation. Cet exemple doit utiliser le gestionnaire d'événements OnInit (plutôt que le constructeur) pour garantir que la page aspx compilée JIT a BindToControl défini.
remplacement protégé void OnInit (EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += new EventHandler(BoundControl_DataBound);
base.OnInit(e);
...
}
L'opération de recherche dans le contrôle de présentation est complétée par la recherche du contrôle Parent du contrôle de pagination. Ici, le Parent est la page elle-même. Il est dangereux d'utiliser Parent de cette manière. Par exemple, si le contrôle de pagination est intégré dans un autre contrôle, tel qu'un contrôle Table, la référence Parent sera en réalité une référence au contrôle Table. Étant donné que la méthode FindControl recherche uniquement la collection de contrôles actuelle, il est impossible d’effectuer une recherche à moins que le contrôle de présentation ne soit dans la collection. Une approche plus sûre consiste à effectuer une recherche récursive dans la collection de contrôles jusqu'à ce que le contrôle cible soit trouvé.
Après avoir trouvé le BoundControl, nous enregistrons le contrôle de pagination en tant qu'écouteur pour l'événement DataBinding. Puisque le contrôle de pagination fonctionne sur une source de données, il est important que ce gestionnaire d'événements soit le dernier de la chaîne d'appel. Cependant, tant que le contrôle de présentation enregistre le gestionnaire d'événements DataBinding dans le gestionnaire d'événements OnInit (comportement par défaut), il n'y aura aucun problème lorsque le contrôle de pagination exploitera la source de données.
Le gestionnaire d'événements DataBound est responsable de l'obtention de la propriété DataSource du contrôle de présentation.
private void BoundControl_DataBound (expéditeur d'objet, System.EventArgs e)
{
si (HasParentControlCalledDataBinding) retourne ;
Tapez type = expéditeur.GetType();
_datasource = type.GetProperty("DataSource");
si (_datasource == null)
throw new NotSupportedException("Le contrôle de pagination nécessite que le contrôle de présentation contienne une DataSource.");
données d'objet = _datasource.GetGetMethod().Invoke(sender,null);
_builder = Adaptateurs[data.GetType()] ;
si (_builder == null)
throw new NullReferenceException("L'adaptateur approprié n'est pas installé pour gérer le type de source de données suivant : "+data.GetType());
_builder.Source = data;
ApplyDataSensitivityRules();
LierParent();
RaiseEvent(DataUpdate,this);
}
Dans DataBound, nous essayons d'obtenir la propriété DataSource via l'API Reflection, puis de renvoyer une référence à la source de données réelle. Maintenant que la source de données est connue, le contrôle de pagination doit également savoir comment opérer sur la source de données. Afin de rendre le contrôle de pagination indépendant d'un contrôle de présentation spécifique, le problème est beaucoup plus compliqué. Cependant, rendre le contrôle de pagination dépendant d’une source de données spécifique va à l’encontre de l’objectif de conception d’un contrôle de pagination flexible. Nous devons utiliser une architecture de plug-in pour garantir que le contrôle de pagination peut gérer diverses sources de données, qu'il s'agisse de la source de données fournie par .NET ou d'une source de données personnalisée.
Afin de fournir une architecture enfichable robuste et évolutive, nous construirons une solution en utilisant le modèle [GoF] Builder.
Figure 4
L'interface IDataSourceAdapter définit les éléments les plus élémentaires requis pour le contrôle de pagination afin d'exploiter les données, ce qui équivaut à un « plug ».
publicinterface IDataSourceAdapter
{
int TotalCount{get;}
objet GetPagedData (int début, int fin);
}
La propriété TotalCount renvoie le nombre total d'éléments contenus dans la source de données avant de traiter les données, tandis que la méthode GetPagedData renvoie un sous-ensemble des données d'origine. Par exemple : en supposant que la source de données est un tableau contenant 20 éléments, le contrôle de pagination s'affiche. les données sous forme de 10 éléments par page, alors le sous-ensemble d'éléments de la première page est constitué des éléments du tableau 0 à 9 et le sous-ensemble d'éléments de la deuxième page est constitué des éléments du tableau 10 à 19. DataViewAdapter fournit un plug de type DataView :
classe interne DataViewAdapter:IDataSourceAdapter
{
_view DataView privée ;
DataViewAdapter interne (vue DataView)
{
_view = vue ;
}
public int TotalCount
{
get{return (_view == null) ? 0 : _view.Table.Rows.Count;}
}
objet public GetPagedData (int début, int fin)
{
Table DataTable = _view.Table.Clone();
pour (int i = début;i<=fin && i<= TotalCount;i++)
{
table.ImportRow(_view[i-1].Row);
}
table de retour ;
}
}
DataViewAdapter implémente la méthode GetPagedData de IDataSourceAdapter, qui clone le DataTable d'origine et importe les données du DataTable d'origine dans le nouveau DataTable. La visibilité de cette classe est intentionnellement définie sur interne afin de masquer les détails d'implémentation aux développeurs Web et de fournir une interface plus simple via la classe Builder.
classe abstraite publique AdapterBuilder
{
objet privé _source ;
vide privé CheckForNull()
{
if (_source == null) throw new NullReferenceException("Une source de données légale doit être fournie");
}
objet virtuel public Source
{
obtenir
{
CheckForNull();
retourner _source ;}
ensemble
{
_source = valeur ;
CheckForNull();
}
}
Adaptateur IDataSourceAdapter abstrait public{get;}
}
La classe abstraite AdapterBuilder fournit une interface plus gérable pour le type IdataSourceAdapter. En raison du niveau d'abstraction accru, nous n'avons plus besoin d'utiliser directement IdataSourceAdapter. En même temps, AdapterBuilder fournit également des instructions pour effectuer un prétraitement avant la pagination des données. De plus, ce Builder rend également la classe d'implémentation réelle, telle que DataViewAdapter, transparente pour les utilisateurs du contrôle de pagination :
public class DataTableAdapterBuilder:AdapterBuilder
{
adaptateur DataViewAdapter privé ;
adaptateur DataViewAdapter privé ;
{
obtenir
{
si (_adapter == null)
{
Table DataTable = (DataTable) Source ;
_adapter = nouveau DataViewAdapter(table.DefaultView);
}
return _adapter;
}
}
remplacement public de l'adaptateur IDataSourceAdapter
{
obtenir
{
retourner ViewAdapter ;
}
}
}
classe publique DataViewAdapterBuilder:AdapterBuilder
{
adaptateur DataViewAdapter privé ;
adaptateur DataViewAdapter privé ;
{
obtenir
{ // Instanciation retardée
si (_adapter == null)
{
_adapter = nouveau DataViewAdapter((DataView)Source);
}
return _adapter;
}
}
remplacement public de l'adaptateur IDataSourceAdapter
{
obtenir{return ViewAdapter;}
}
}
Le type DataView et le type DataTable sont si étroitement liés qu'il peut être judicieux de construire un DataAdapter générique. En fait, il suffit d'ajouter un autre constructeur qui gère le DataTable. Malheureusement, lorsque les utilisateurs ont besoin de fonctionnalités différentes pour gérer un DataTable, la classe entière doit être remplacée ou héritée. Si nous construisons un nouveau Builder qui utilise le même IdataSourceAdapter, l'utilisateur a plus de liberté pour choisir comment implémenter l'adaptateur.
Dans le contrôle de pagination, l'opération de recherche de la classe Builder appropriée est complétée par une collection de type sécurisé.
classe publique AdapterCollection:DictionaryBase
{
chaîne privée GetKey (clé de type)
{
retourner la clé.FullName;
}
public AdapterCollection() {}
publicvoid Ajouter (clé de type, valeur AdapterBuilder)
{
Dictionary.Add (GetKey (clé), valeur);
}
publicbool contient (clé de type)
{
return Dictionary.Contains(GetKey(key));
}
publicvoid Supprimer (clé de type)
{
Dictionnaire. Supprimer (GetKey (clé));
}
public AdapterBuilder this[Tapez la clé]
{
get{return (AdapterBuilder)Dictionary[GetKey(key)];}
set{Dictionnaire[GetKey(key)]=valeur;}
}
}
AdapterCollection s'appuie sur le type DataSource et DataSource est intelligemment introduit via BoundControl_DataBound. La clé d'index utilisée ici est la méthode Type.FullName, qui garantit l'unicité de la clé d'index de chaque type. En même temps, elle attribue également la responsabilité de garantir qu'il n'y a qu'un seul Builder pour chaque type à AdapterCollection. Ajoutez la recherche Builder à la méthode BoundControl_DataBound et les résultats sont les suivants :
public AdapterCollection Adapters
{
obtenir{return _adapters;}
}
bool privé HasParentControlCalledDataBinding
{
obtenir{return _builder != null;}
}
private void BoundControl_DataBound (expéditeur d'objet, System.EventArgs e)
{
si (HasParentControlCalledDataBinding) retourne ;
Tapez type = expéditeur.GetType();
_datasource = type.GetProperty("DataSource");
si (_datasource == null)
throw new NotSupportedException("Le contrôle de pagination nécessite que le contrôle de présentation contienne une DataSource.");
données d'objet = _datasource.GetGetMethod().Invoke(sender,null);
_builder = Adaptateurs[data.GetType()] ;
si (_builder == null)
throw new NullReferenceException("L'adaptateur approprié n'est pas installé pour gérer le type de source de données suivant : "+data.GetType());
_builder.Source = data;
ApplyDataSensitivityRules();
LierParent();
RaiseEvent(DataUpdate,this);
}
La méthode BoundControl_DataBound utilise HasParentControlCalledDataBinding pour vérifier si le Builder a été créé. Si tel est le cas, elle n'effectuera plus l'opération de recherche du Builder approprié. L'initialisation de la table Adapters se fait dans le constructeur :
public Pager()
{
SelectedPager=nouveau 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());
}
La dernière méthode à implémenter est BindParent, qui est utilisée pour traiter et renvoyer les données.
vide privé BindParent()
{
_datasource.GetSetMethod().Invoke(BoundControl,
nouvel objet[]{_builder.Adapter.GetPagedData(StartRow,ResultsToShow*CurrentPage)});
}
Cette méthode est très simple, car le traitement des données est réellement effectué par l'adaptateur. Une fois ce processus terminé, nous utiliserons à nouveau l'API Reflection, mais cette fois pour définir la propriété DataSource du contrôle de présentation.
3. Conception de l'interface
Jusqu'à présent, les fonctions principales du contrôle de pagination ont été presque implémentées, mais s'il y a un manque de méthodes de présentation appropriées, le contrôle de pagination ne sera pas très utile.
Afin de séparer efficacement la méthode de présentation de la logique du programme, le meilleur moyen est d'utiliser des modèles, ou pour être plus précis, d'utiliser l'interface Itemplate. En fait, Microsoft comprend clairement la puissance des modèles et les utilise presque partout, même dans l'analyseur de page lui-même. Malheureusement, les modèles ne sont pas un concept aussi simple que certains le pensent, et il faut un certain temps pour vraiment en comprendre l'essence. Heureusement, il existe de nombreuses informations dans ce domaine, je n'entrerai donc pas dans les détails ici. De retour au contrôle de pagination, il comporte quatre boutons : page d'accueil, page précédente, page suivante, dernière page, et bien sûr le numéro de chaque page. Les quatre boutons de navigation sont sélectionnés dans la classe ImageButton au lieu de la classe LinkButton. Du point de vue de la conception Web professionnelle, les boutons graphiques sont évidemment plus utiles que les liens monotones.
public ImageButton FirstButton{get {return First;}}
public ImageButton LastButton{get {return Last;}}
public ImageButton BoutonPrécédent{get {return Précédent;}}
public ImageButton NextButton{get {return Next;}}
Les numéros de page sont construits dynamiquement car ils dépendent du nombre d'enregistrements dans la source de données et du nombre d'enregistrements affichés sur chaque page. Le numéro de page sera ajouté à un panneau et les concepteurs Web pourront utiliser le panneau pour spécifier où afficher le numéro de page. Le processus de création de numéros de page sera discuté en détail plus tard. Nous devons maintenant fournir un modèle pour le contrôle de pagination afin que les utilisateurs puissent personnaliser l'apparence du contrôle de pagination.
[Conteneur de modèles (type de (LayoutContainer))]
Disposition ITemplate publique
{
obtenir{retour (_layout;}
définir{_layout = valeur;}
}
classe publique LayoutContainer:Control,INamingContainer
{
publicLayoutContainer()
{this.ID = "Page";}
}
La classe LayoutContainer fournit un conteneur pour les modèles. De manière générale, il est toujours bon d'ajouter un identifiant personnalisé au conteneur de modèles, ce qui évitera des problèmes lors de la gestion des événements et des appels de page. Le diagramme UML suivant décrit le mécanisme de présentation du contrôle de pagination.
Figure 5
La première étape de la création d'un modèle consiste à définir la mise en page dans la page aspx :
<MISE EN PAGE>
<asp:ImageButton id="First" Runat="server" imageUrl="play2L_dis.gif"
AlternateText="Accueil"></asp:ImageButton>
<asp:ImageButton id="Précédent" Runat="server" imageUrl="play2L.gif"
AlternateText="Page précédente"></asp:ImageButton>
<asp:ImageButton id="Suivant" Runat="server" imageUrl="play2.gif"
AlternateText="Page suivante"></asp:ImageButton>
<asp:ImageButton id="Last" Runat="server" imageUrl="play2_dis.gif"
AlternateText="Dernière page"></asp:ImageButton>
<asp:Panel id="Pager" Runat="serveur"></asp:Panel>
</LAYOUT>
Cet exemple de mise en page ne contient aucun élément de format, tel que des tableaux, etc. Bien sûr, les applications réelles peuvent (et doivent) ajouter des éléments de format, veuillez consulter plus d'instructions plus tard.
L'interface Itemplate ne fournit qu'une seule méthode InstantiateIn, qui analyse le modèle et lie le conteneur.
vide privé InstantiateTemplate()
{
_container = nouveau LayoutContainer();
Layout.InstantiateIn(_container);
Premier = (ImageButton)_container.FindControl("Premier");
Précédent = (ImageButton)_container.FindControl("Précédent");
Suivant = (ImageButton)_container.FindControl("Suivant");
Dernier = (ImageButton)_container.FindControl("Dernier");
Holder = (Panel)_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);
}
La première chose que fait la méthode InstatiateTemplate du contrôle est d'instancier le modèle, c'est-à-dire d'appeler Layout.InstantiateIn(_container). Un conteneur est en fait une sorte de contrôle et son utilisation est similaire à celle des autres contrôles. La méthode InstantiateTemplate utilise cette fonctionnalité pour rechercher les quatre boutons de navigation et le panneau utilisé pour contenir le numéro de page. Les boutons de navigation sont trouvés par leurs ID. Il s'agit d'une petite restriction sur les contrôles de pagination : les boutons de navigation doivent avoir des ID spécifiés, qui sont Premier, Précédent, Suivant et Dernier. De plus, l'ID du panneau doit être Pager, sinon ce ne sera pas le cas. trouvé. Malheureusement, cela semble être la meilleure approche pour le mécanisme de présentation que nous avons choisi ; mais nous espérons qu'avec une documentation appropriée, cette limitation mineure ne posera aucun problème. Une autre alternative consiste à laisser chaque bouton hériter de la classe ImageButton, définissant ainsi un nouveau type puisque chaque bouton est d'un type différent, une recherche récursive peut être implémentée dans le conteneur pour trouver divers boutons spécifiques, éliminant ainsi le besoin d'utiliser l'ID du bouton ; attribut.
Après avoir trouvé les quatre boutons, associez-leur les gestionnaires d’événements appropriés. Une décision importante doit être prise ici, à savoir quand appeler InstantiateTemplate. Généralement, ce type de méthode doit être appelé dans la méthode CreateChildControls, car l'objectif principal de la méthode CreateChildControls est ce type de tâche de création de contrôles enfants. Étant donné que le contrôle de pagination ne modifie jamais ses contrôles enfants, il n'a pas besoin de la fonctionnalité fournie par CreateChildControls pour modifier l'état d'affichage en fonction d'un événement. Plus le contrôle enfant s'affiche rapidement, mieux c'est, donc l'endroit idéal pour appeler la méthode InstantiateTemplate est dans l'événement OnInit.
remplacement protégé void OnInit (EventArgs e)
{
_boundcontrol = Parent.FindControl(BindToControl);
BoundControl.DataBinding += new EventHandler(BoundControl_DataBound);
InstantiateTemplate();
Contrôles.Add(_container);
base.OnInit(e);
}
En plus d'appeler la méthode InstantiateTemplate, la méthode OnInit a également une autre tâche importante consistant à ajouter le conteneur au contrôle de pagination. Si vous n'ajoutez pas le conteneur à la collection de contrôles du pager, le modèle ne sera pas affiché car la méthode Render ne sera jamais appelée.
Les modèles peuvent également être définis par programme en implémentant l'interface Itemplate. En plus d'être une mesure visant à accroître la flexibilité, cette fonctionnalité peut également fournir un modèle par défaut à utiliser lorsque l'utilisateur ne fournit pas de modèle via une page ASPX.
classe publique DefaultPagerLayout: ITemplate
{
privé ImageButton Suivant ;
privé ImageButton d'abord ;
privé ImageButton Dernier ;
privé ImageButton Précédent ;
Téléavertisseur de panneau privé ;
public DefaultPagerLayout()
{
Suivant = new ImageButton();
Premier = new ImageButton();
Dernier = nouveau ImageButton();
Précédent = new ImageButton();
Pager = new Panel();
Next.ID="Suivant"; Next.AlternateText="Page suivante";Next.ImageUrl="play2.gif";
First.ID="Premier"; First.AlternateText="Accueil";First.ImageUrl="play2L_dis.gif";
Last.ID = "Dernière"; Last.AlternateText = "Dernière page" Last.ImageUrl="play2_dis.gif";
Précédent.ID="Précédent"; Précédent.AlternateText="Page précédente";Précédent.ImageUrl="play2L.gif";
Pager.ID="Pager";
}
public void InstantiateIn (contrôle de contrôle)
{
control.Controls.Clear();
Table table = nouvelle Table();
table.BorderWidth = Unit.Pixel(0);
table.CellSpacing= 1 ;
table.CellPadding =0;
Ligne TableRow = new TableRow();
rangée.VerticalAlign = VerticalAlign.Top;
table.Rows.Add(ligne);
Cellule TableCell = new TableCell();
cell.HorizontalAlign = HorizontalAlign.Right;
cell.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add(Premier);
cell.Controls.Add (Précédent);
rangée.Cells.Add(cellule);
cellule = nouveau TableCell();
cell.HorizontalAlign=HorizontalAlign.Center;
cell.Controls.Add(Pager);
rangée.Cells.Add(cellule);
cellule = nouveau TableCell();
cell.VerticalAlign = VerticalAlign.Middle;
cell.Controls.Add (Suivant);
cell.Controls.Add(Dernier);
rangée.Cells.Add(cellule);
control.Controls.Add(table);
}
}
DefaultPagerLayout fournit tous les éléments de navigation par programme et les ajoute à la page aspx, mais cette fois, les éléments de navigation sont formatés avec des tableaux HTML standard. Désormais, si l'utilisateur ne fournit pas de modèle de présentation, le programme fournira automatiquement un modèle par défaut.
[TemplateContainer(typeof(LayoutContainer))]
Disposition ITemplate publique
{
get{return (_layout == null) ? nouveau DefaultPagerLayout():_layout;}
définir{_layout = valeur;}
}
Jetons un coup d'œil au processus de génération de chaque numéro de page. Le contrôle de pagination doit d'abord déterminer certaines valeurs de propriété et utiliser ces valeurs de propriété pour déterminer le nombre de numéros de page différents à générer.
public int PageActuelle
{
obtenir
{
chaîne cur = (chaîne)ViewState["CurrentPage"];
return (cur == string.Empty || cur ==null) 1 : int.Parse(cur);
}
ensemble
{
ViewState["CurrentPage"] = valeur.ToString();}
}
public int PagersToShow
{
obtenir{return _results;}
définir{_results = valeur;}
}
public int RésultatsÀAfficher
{
obtenir{return _resultsperpage;}
définir{_resultsperpage = valeur;}
}
CurrentPage enregistre en fait la page actuelle dans le ViewState du numéro de page. Les propriétés définies par la méthode PagersToShow permettent à l'utilisateur de spécifier le nombre de pages à afficher, tandis que les propriétés définies par ResultsToShow permettent à l'utilisateur de spécifier le nombre d'enregistrements à afficher. chaque page. La valeur par défaut est 10.
NumberofPagersToGenerate renvoie le nombre actuel de numéros de page qui doivent être générés.
private int PagerSequence
{
obtenir
{
retourner Convert.ToInt32
(Math.Ceiling((double)CurrentPage/(double)PagersToShow));}
}
privé int NumberOfPagersToGenerate
{
obtenir{return PagerSequence*PagersToShow;}
}
privé entier TotalPagesToShow
{
get{return Convert.ToInt32(Math.Ceiling((double)TotalResults/(double)_resultsperpage));}
}
public int TotalRésultats
{
obtenir{return _builder.Adapter.TotalCount;}
}
La méthode TotalPagesToShow renvoie le nombre total de pages à afficher, ajusté par la propriété ResultsToShow prédéfinie de l'utilisateur.
Bien qu'ASP.NET définisse certains styles par défaut, ils peuvent ne pas être très pratiques pour les utilisateurs de contrôles de pagination. Les utilisateurs peuvent ajuster l'apparence du contrôle de pagination via des styles personnalisés.
public Style UnSelectedPagerStyle {get {return UnselectedPager;}}
public Style SelectedPagerStyle {get {return SelectedPager;}}
UnSelectedPagerStyle fournit le style utilisé lorsque le numéro de page n'est pas sélectionné, et SelectedPagerStyle fournit le style utilisé lorsque le numéro de page est sélectionné.
private void GeneratePagers (contrôle WebControl)
{
control.Controls.Clear();
int pager = (PagerSequence-1)* PagersToShow +1;
pour (;pager<=NumberOfPagersToGenerate && pager<=TotalPagesToShow;pager++)
{
Lien LinkButton = nouveau LinkButton();
lien.Text = pager.ToString();
lien.ID = pager.ToString();
link.Click += new EventHandler(this.Pager_Click);
si (link.ID.Equals(CurrentPage.ToString()))
lien.MergeStyle(SelectedPagerStyle);
autre
lien.MergeStyle(UnSelectedPagerStyle);
control.Controls.Add(link);
control.Controls.Add(new LiteralControl(" "));
}
}
vide privé GeneratePagers()
{
Générer des pages (titulaire);
}
La méthode GeneratePagers crée dynamiquement tous les numéros de page, qui sont des boutons de type LinkButton. Les attributs d'étiquette et d'ID de chaque numéro de page sont attribués via une boucle et, en même temps, l'événement click est lié au gestionnaire d'événements approprié. Enfin, le numéro de page est ajouté à un contrôle conteneur – dans ce cas, un objet Panel. L'ID du bouton sert à identifier quel bouton a déclenché l'événement de clic. Voici la définition du gestionnaire d'événements :
private void Pager_Click(object sender, System.EventArgs e)
{
Bouton LinkButton = (LinkButton) expéditeur ;
CurrentPage = int.Parse(bouton.ID);
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Pager));
Mise à jour();
}
private void Next_Click (expéditeur de l'objet, System.Web.UI.ImageClickEventArgs e)
{
si (Page actuelle<TotalPagesToShow)
PageActuelle++;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Next));
Mise à jour();
}
private void Previous_Click (expéditeur de l'objet, System.Web.UI.ImageClickEventArgs e)
{
si (PageActuelle > 1)
PageActuelle--;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Previous));
Mise à jour();
}
private void First_Click (expéditeur de l'objet, System.Web.UI.ImageClickEventArgs e)
{
PageActuelle = 1 ;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.First));
Mise à jour();
}
private void Last_Click (expéditeur de l'objet, System.Web.UI.ImageClickEventArgs e)
{
PageActuelle = TotalPagesToShow ;
RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Last));
Mise à jour();
}
Ces gestionnaires d'événements sont similaires dans le sens où ils modifient d'abord la page actuelle du contrôle de pagination, puis actualisent le contrôle lié.
Mise à jour du vide privé ()
{
si (!HasParentControlCalledDataBinding) retourne ;
ApplyDataSensitivityRules();
LierParent();
BoundControl.DataBind();
}
Tout d'abord, le contrôle de pagination vérifie si les adaptateurs nécessaires ont été initialisés en appelant la méthode HasParentControlCalledDataBinding. Si tel est le cas, appliquez les règles indiquées précédemment pour ajuster automatiquement les contrôles en fonction des conditions d’affichage des données au contrôle actuel. Ces règles font que le contrôle de pagination se comporte différemment en fonction des différentes conditions des données dans BoundControl. Bien que ces règles soient contrôlées en interne par le contrôle de pagination, elles peuvent être facilement déplacées hors du contrôle à l'aide du mode [GoF] State lorsque cela est nécessaire.
public bool IsDataSensitive
{
obtenir{return _isdatasensitive;}
set{_isdatasensitive = valeur;}
}
booléen privé IsPagerVisible
{
get{return (TotalPagesToShow != 1) && IsDataSensitive;}
}
bool privé IsPreviousVisible
{
obtenir
{
return (!IsDataSensitive) ?
(PageActuelle != 1);
}
}
booléen privé IsNextVisible
{
obtenir
{
return (!IsDataSensitive) ?
(PageActuelle != TotalPagesToShow);
}
}
vide privé ApplyDataSensitivityRules()
{
FirstButton.Visible = IsPreviousVisible ;
BoutonPrécédent.Visible = IsPrécédentVisible ;
LastButton.Visible = IsNextVisible ;
NextButton.Visible = IsNextVisible ;
if (IsPagerVisible) GeneratePagers();
}
La méthode ApplyDataSensitivityRules implémente des règles prédéfinies telles que IsPagerVisible, IsPreviousVisible et IsNextVisible. Par défaut, ces règles seront activées pour le contrôle de pagination, mais l'utilisateur peut les désactiver en définissant la propriété IsDataSensitive.
Jusqu'à présent, la partie affichage du contrôle de pagination a été essentiellement conçue. Le dernier travail de finition restant consiste à fournir plusieurs gestionnaires d'événements afin que les utilisateurs puissent effectuer les ajustements nécessaires lorsque divers événements de contrôle de pagination se produisent.
délégué public void PageDelegate (expéditeur d'objet, PageChangedEventArgs e);
public enum PagedEventInvoker {Next, Previous, First, Last, Pager}
classe publique PageChangedEventArgs: EventArgs
{
nouvelle page int privée ;
Invocateur Enum privé ;
public PageChangedEventArgs(int newpage):base()
{
this.newpage = nouvellepage;
}
public PageChangedEventArgs (int nouvelle page, invocateur PagedEventInvoker)
{
this.newpage = nouvelle page;
this.invoker = invocateur ;
}
public int NouvellePage {get{return newpage;}}
public Enum EventInvoker{get{return invocateur;}}
}
Étant donné que le contrôle de pagination doit renvoyer des paramètres d'événement personnalisés, nous définissons une classe PageChangedEventArgs dédiée. La classe PageChangedEventArgs renvoie le type PagedEventInvoker, qui est un énumérateur des contrôles susceptibles de déclencher l'événement. Afin de gérer les paramètres d'événement personnalisés, nous définissons un nouveau délégué, PageDelegate. L'événement est défini sous la forme suivante :
événement public PageDelegate PageChanged ;
public event EventHandler DataUpdate;
Lorsqu'un événement n'a pas d'écouteur d'événement correspondant, ASP.NET lèvera une exception. Le contrôle de pagination définit les méthodes RaiseEvent suivantes.
private void RaiseEvent (EventHandler e, expéditeur d'objet)
{
this.RaiseEvent(e,this,null);
}
private void RaiseEvent (EventHandler e, expéditeur d'objet, arguments PageChangedEventArgs)
{
si (e! = nul)
{
e (expéditeur, arguments);
}
}
private void RaiseEvent (PageDelegate e, expéditeur d'objet)
{
this.RaiseEvent(e,this,null);
}
private void RaiseEvent (PageDelegate e, expéditeur d'objet, arguments PageChangedEventArgs)
{
si (e! = nul)
{
e (expéditeur, arguments);
}
}
Les gestionnaires d'événements peuvent désormais déclencher des événements en appelant des méthodes Raiseevent individuelles.
4. Exemples d'application
À ce stade, la conception du contrôle de pagination a été terminée et peut être officiellement utilisée. Pour utiliser le contrôle de pagination, liez-le simplement à un contrôle de présentation.
<asp: répéter id = "répéter" runat = "server">
<Modèle d'élément>
Colonne 1:
<% # Convert.toString (databinder.eval (contener.dataitem, "colonnen1"))%>
<br>
Colonne 2:
<% # Convert.toString (databinder.eval (contener.dataitem, "Column2"))%>
<br>
Colonne 3:
<% # Convert.toString (databinder.eval (contener.dataitem, "colonnes3"))%>
<br>
<hr>
</Modèle d'élément>
< / ASP: répéteur >
< cc1: pager id = "pager" resultShow = "2" runat = "server" bindtocontrol = "répéteur" >
< SelectedPagersTyle BackColor = "Yellow" / >
< / CC1: Pager >
La page ASPX ci-dessus lie le contrôle de la pagination à un contrôle de répéteur, définit le nombre d'enregistrements affichés sur chaque page à 2, la couleur du numéro de page sélectionné est jaune et utilise la disposition par défaut. comme le montre la figure 1. Vous trouverez ci-dessous un autre exemple, qui lie le contrôle de pagination à un dataGrid, comme le montre la figure 2.
< ASP: datagrid id = "grid" runat = "server" >< / asp: datagrid >
< cc1: pager id = "pagergrid" resultShow = "2" runat = "server" bindtocontrol = "grid" >
< SelectedPagersTyle BackColor = "Red" >< / SelectedPagersTyle >
< Disposition >
< ASP: imagebutton id = "premier" runat = "server" imageurl = "play2l_dis.gif" alternatetext = "home" >< / asp: imagebutton >
< ASP: imagebutton id = "précédent" runat = "server" imageUrl = "play2l.gif" alternateText = "page précédente" >< / asp: imagebutton >
< ASP: imagebutton id = "next" runat = "server" imageUrl = "play2.gif" alternateText = "page suivante" >< / asp: imagebutton >
< ASP: imagebutton id = "dernier" runat = "server" imageurl = "play2_dis.gif" alternateText = "Last Page" >< / asp: ImageButton >
< ASP: Panneau ID = "Pager" runat = "Server" >< / ASP: Panneau >
< / disposition >
< / CC1: Pager >
Le test montre que le contrôle de pagination ne dépend pas de contrôles de présentation spécifiques. l'exemple complet.
Bien que l'apprentissage de développer des contrôles Web personnalisés ne soit pas une tâche facile, les avantages de la maîtrise de cette compétence sont évidents. Temps.
http://www.cnblogs.com/niit007/archive/2006/08/13/475501.html