Création d’une interface utilisateur de tri personnalisée (VB)
par Scott Mitchell
Lors de l’affichage d’une longue liste de données triées, il peut être très utile de regrouper des données associées en introduisant des lignes de séparateur. Dans ce tutoriel, nous allons voir comment créer une telle interface utilisateur de tri.
Introduction
Lors de l’affichage d’une longue liste de données triées où il n’y a qu’une poignée de valeurs différentes dans la colonne triée, un utilisateur final peut avoir du mal à déterminer où, exactement, les limites de la différence se produisent. Par exemple, il existe 81 produits dans la base de données, mais seulement neuf choix de catégorie différents (huit catégories uniques plus l’option NULL
). Prenons le cas d’un utilisateur qui souhaite examiner les produits qui appartiennent à la catégorie Fruits de mer. À partir d’une page qui répertorie tous les produits d’un seul GridView, l’utilisateur peut décider qu’il vaut mieux trier les résultats par catégorie, ce qui regroupera tous les produits de fruits de mer. Après avoir trié par catégorie, l’utilisateur doit ensuite parcourir la liste, à la recherche de l’emplacement de début et de fin des produits de la mer. Étant donné que les résultats sont classés par ordre alphabétique par le nom de catégorie, trouver les produits de la mer n’est pas difficile, mais il nécessite toujours une analyse étroite de la liste des éléments dans la grille.
Pour aider à mettre en évidence les limites entre les groupes triés, de nombreux sites web utilisent une interface utilisateur qui ajoute un séparateur entre ces groupes. Les séparateurs tels que ceux présentés dans la figure 1 permettent à un utilisateur de trouver plus rapidement un groupe particulier et d’identifier ses limites, ainsi que de déterminer quels groupes distincts existent dans les données.
Figure 1 : Chaque groupe de catégories est clairement identifié (cliquer pour afficher l’image en taille réelle)
Dans ce tutoriel, nous allons voir comment créer une telle interface utilisateur de tri.
Étape 1 : Création d’un GridView standard triable
Avant d’explorer comment augmenter GridView pour fournir l’interface de tri améliorée, nous allons d’abord créer un GridView standard triable qui répertorie les produits. Commencez par ouvrir la CustomSortingUI.aspx
page dans le PagingAndSorting
dossier. Ajoutez un GridView à la page, définissez sa ID
propriété sur ProductList
et liez-la à un nouvel ObjetDataSource. Configurez ObjectDataSource pour qu’il utilise la ProductsBLL
méthode de classe s GetProducts()
pour sélectionner des enregistrements.
Ensuite, configurez GridView de telle sorte qu’il contienne uniquement les ProductName
CategoryName
, , SupplierName
et UnitPrice
BoundFields et le CheckBoxField abandonné. Enfin, configurez GridView pour prendre en charge le tri en cochant la case Activer le tri dans la balise intelligente de GridView (ou en définissant sa AllowSorting
propriété sur true
). Après avoir effectué ces ajouts à la CustomSortingUI.aspx
page, le balisage déclaratif doit ressembler à ce qui suit :
<asp:GridView ID="ProductList" runat="server" AllowSorting="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" EnableViewState="False">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:C}"
HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts"
TypeName="ProductsBLL"></asp:ObjectDataSource>
Prenez un moment pour voir notre progression jusqu’à présent dans un navigateur. La figure 2 montre le GridView triable lorsque ses données sont triées par catégorie par ordre alphabétique.
Figure 2 : Les données de GridView triable sont triées par catégorie (cliquez pour afficher l’image en taille réelle)
Étape 2 : Exploration des techniques d’ajout des lignes de séparateur
Une fois le GridView générique triable terminé, il ne reste plus qu’à ajouter les lignes de séparateur dans gridView avant chaque groupe trié unique. Mais comment ces lignes peuvent-elles être injectées dans gridView ? Essentiellement, nous devons itérer dans les lignes de GridView, déterminer où se produisent les différences entre les valeurs de la colonne triée, puis ajouter la ligne de séparateur appropriée. Lorsque vous réfléchissez à ce problème, il semble naturel que la solution se trouve quelque part dans le gestionnaire d’événements GridView RowDataBound
. Comme nous l’avons vu dans le tutoriel Mise en forme personnalisée basée sur les données , ce gestionnaire d’événements est couramment utilisé lors de l’application d’une mise en forme au niveau des lignes en fonction des données de ligne. Toutefois, le RowDataBound
gestionnaire d’événements n’est pas la solution ici, car les lignes ne peuvent pas être ajoutées au GridView par programmation à partir de ce gestionnaire d’événements. En fait, la collection gridView est Rows
en lecture seule.
Pour ajouter des lignes supplémentaires à GridView, nous avons trois choix :
- Ajouter ces lignes de séparateur de métadonnées aux données réelles liées à GridView
- Une fois gridView lié aux données, ajoutez des instances supplémentaires
TableRow
à la collection de contrôles GridView - Créer un contrôle serveur personnalisé qui étend le contrôle GridView et remplace les méthodes responsables de la construction de la structure de GridView
La création d’un contrôle serveur personnalisé serait la meilleure approche si cette fonctionnalité était nécessaire sur de nombreuses pages web ou sur plusieurs sites web. Cependant, cela impliquerait un peu de code et une exploration approfondie des profondeurs du fonctionnement interne de GridView. Par conséquent, nous ne considérerons pas cette option pour ce tutoriel.
Les deux autres options ajoutant des lignes de séparateur aux données réelles liées à GridView et manipulant la collection de contrôles GridView après sa limite , attaquez le problème différemment et méritent une discussion.
Ajout de lignes aux données liées à GridView
Lorsque GridView est lié à une source de données, il crée un GridViewRow
pour chaque enregistrement retourné par la source de données. Par conséquent, nous pouvons injecter les lignes de séparateur nécessaires en ajoutant des enregistrements de séparateur à la source de données avant de la lier à GridView. La figure 3 illustre ce concept.
Figure 3 : Une technique implique l’ajout de lignes séparateurs à la source de données
J’utilise le terme d’enregistrements séparateurs entre guillemets, car il n’y a pas d’enregistrement de séparateur spécial ; au lieu de cela, nous devons indiquer qu’un enregistrement particulier dans la source de données sert de séparateur plutôt qu’une ligne de données normale. Pour nos exemples, nous relions un ProductsDataTable
instance au GridView, qui est composé de ProductRows
. Nous pouvons marquer un enregistrement en tant que ligne de séparation en définissant sa CategoryID
propriété sur -1
(car une telle valeur ne pouvait pas exister normalement).
Pour utiliser cette technique, nous devons effectuer les étapes suivantes :
- Récupérer par programmation les données à lier à GridView (un
ProductsDataTable
instance) - Trier les données en fonction des propriétés et
SortDirection
des propriétés GridViewSortExpression
- Itérer à travers dans
ProductsRows
leProductsDataTable
, en recherchant où se trouvent les différences dans la colonne triée - À chaque limite de groupe, injectez un enregistrement
ProductsRow
séparateur instance dans le DataTable, celui dont il estCategoryID
défini-1
sur (ou toute désignation a été décidée pour marquer un enregistrement en tant qu’enregistrement séparateur) - Après avoir injecté les lignes de séparateur, lier par programmation les données à GridView
En plus de ces cinq étapes, nous devons également fournir un gestionnaire d’événements pour l’événement GridView.RowDataBound
Ici, nous d case activée chacun DataRow
et de déterminer s’il s’agit d’une ligne de séparation, dont CategoryID
le paramètre est -1
. Si c’est le cas, nous souhaitons probablement ajuster sa mise en forme ou le texte affiché dans les cellules.
L’utilisation de cette technique pour injecter les limites du groupe de tri nécessite un peu plus de travail que ce qui est décrit ci-dessus, car vous devez également fournir un gestionnaire d’événements pour l’événement Sorting
GridView et effectuer le SortExpression
suivi des valeurs et SortDirection
.
Manipulation de la collection de contrôles GridView après le trafic de données
Au lieu de envoyer des messages aux données avant de les lier à GridView, nous pouvons ajouter les lignes de séparateur une fois que les données ont été liées à GridView. Le processus de liaison de données crée la hiérarchie de contrôle de GridView, qui en réalité est simplement une Table
instance composée d’une collection de lignes, chacune composée d’une collection de cellules. Plus précisément, la collection de contrôles GridView contient un Table
objet à sa racine, un GridViewRow
(qui est dérivé de la TableRow
classe) pour chaque enregistrement dans le DataSource
lié au GridView, et un TableCell
objet dans chaque GridViewRow
instance pour chaque champ de données dans le DataSource
.
Pour ajouter des lignes de séparateur entre chaque groupe de tri, nous pouvons manipuler directement cette hiérarchie de contrôle une fois qu’elle a été créée. Nous pouvons être sûrs que la hiérarchie de contrôle de GridView a été créée pour la dernière fois au moment où la page est affichée. Par conséquent, cette approche remplace la Page
méthode de classe s Render
, à laquelle la hiérarchie de contrôle finale de GridView est mise à jour pour inclure les lignes de séparateur nécessaires. La figure 4 illustre ce processus.
Figure 4 : Une autre technique manipule la hiérarchie de contrôles GridView (cliquez pour afficher l’image en taille réelle)
Pour ce tutoriel, nous allons utiliser cette dernière approche pour personnaliser l’expérience utilisateur de tri.
Notes
Le code que je présente dans ce tutoriel est basé sur l’exemple fourni dans l’entrée de blog de Teemu Keiski , Playing a Bit with GridView Sort Grouping.
Étape 3 : Ajout des lignes de séparateur à la hiérarchie de contrôles GridView
Étant donné que nous voulons uniquement ajouter les lignes de séparateur à la hiérarchie de contrôle GridView une fois que sa hiérarchie de contrôle a été créée et créée pour la dernière fois lors de cette visite de page, nous voulons effectuer cet ajout à la fin du cycle de vie de la page, mais avant que la hiérarchie de contrôle GridView réelle ait été rendue en HTML. Le dernier point possible auquel nous pouvons effectuer cette opération est l’événement Page
de classe s Render
, que nous pouvons remplacer dans notre classe code-behind à l’aide de la signature de méthode suivante :
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Add code to manipulate the GridView control hierarchy
MyBase.Render(writer)
End Sub
Lorsque la méthode d’origine Render
de la Page
classe est appeléebase.Render(writer)
, chacun des contrôles de la page est rendu, ce qui génère le balisage en fonction de leur hiérarchie de contrôles. Par conséquent, il est impératif que nous appelons base.Render(writer)
tous les deux , afin que la page soit rendue, et que nous manipulons la hiérarchie de contrôles GridView avant d’appeler base.Render(writer)
, afin que les lignes de séparateur aient été ajoutées à la hiérarchie de contrôle GridView avant qu’elle ne soit rendue.
Pour injecter les en-têtes de groupe de tri, nous devons d’abord nous assurer que l’utilisateur a demandé que les données soient triées. Par défaut, le contenu de GridView n’est pas trié. Par conséquent, nous n’avons pas besoin d’entrer d’en-têtes de tri de groupe.
Notes
Si vous souhaitez que le GridView soit trié par une colonne particulière lors du premier chargement de la page, appelez la méthode GridView s lors de Sort
la première visite de page (mais pas sur les publications ultérieures). Pour ce faire, ajoutez cet appel dans le gestionnaire d’événements Page_Load
dans un if (!Page.IsPostBack)
conditionnel. Pour plus d’informations sur la Sort
méthode, reportez-vous au didacticiel Pagination et tri des données de rapport.
En supposant que les données ont été triées, notre tâche suivante consiste à déterminer la colonne par laquelle les données ont été triées, puis à analyser les lignes à la recherche de différences dans les valeurs de cette colonne. Le code suivant garantit que les données ont été triées et recherche la colonne selon laquelle les données ont été triées :
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' Determine the index and HeaderText of the column that
'the data is sorted by
Dim sortColumnIndex As Integer = -1
Dim sortColumnHeaderText As String = String.Empty
For i As Integer = 0 To ProductList.Columns.Count - 1
If ProductList.Columns(i).SortExpression.CompareTo( _
ProductList.SortExpression) = 0 Then
sortColumnIndex = i
sortColumnHeaderText = ProductList.Columns(i).HeaderText
Exit For
End If
Next
' TODO: Scan the rows for differences in the sorted column�s values
End Sub
Si le GridView n’a pas encore été trié, la propriété GridView n’aura SortExpression
pas été définie. Par conséquent, nous voulons uniquement ajouter les lignes de séparateur si cette propriété a une valeur. Si c’est le cas, nous devons ensuite déterminer l’index de la colonne selon laquelle les données ont été triées. Pour ce faire, effectuez une boucle dans la collection gridView, Columns
en recherchant la colonne dont SortExpression
la propriété est égale à la propriété de SortExpression
GridView. En plus de l’index s de colonne, nous récupérons également la HeaderText
propriété, qui est utilisée lors de l’affichage des lignes de séparateur.
Avec l’index de la colonne selon laquelle les données sont triées, l’étape finale consiste à énumérer les lignes du GridView. Pour chaque ligne, nous devons déterminer si la valeur de colonne triée diffère de la valeur de colonne triée de la ligne précédente. Dans ce cas, nous devons injecter une nouvelle GridViewRow
instance dans la hiérarchie de contrôle. Pour ce faire, utilisez le code suivant :
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' ... Code for finding the sorted column index removed for brevity ...
' Reference the Table the GridView has been rendered into
Dim gridTable As Table = CType(ProductList.Controls(0), Table)
' Enumerate each TableRow, adding a sorting UI header if
' the sorted value has changed
Dim lastValue As String = String.Empty
For Each gvr As GridViewRow In ProductList.Rows
Dim currentValue As String = gvr.Cells(sortColumnIndex).Text
If lastValue.CompareTo(currentValue) <> 0 Then
' there's been a change in value in the sorted column
Dim rowIndex As Integer = gridTable.Rows.GetRowIndex(gvr)
' Add a new sort header row
Dim sortRow As New GridViewRow(rowIndex, rowIndex, _
DataControlRowType.DataRow, DataControlRowState.Normal)
Dim sortCell As New TableCell()
sortCell.ColumnSpan = ProductList.Columns.Count
sortCell.Text = String.Format("{0}: {1}", _
sortColumnHeaderText, currentValue)
sortCell.CssClass = "SortHeaderRowStyle"
' Add sortCell to sortRow, and sortRow to gridTable
sortRow.Cells.Add(sortCell)
gridTable.Controls.AddAt(rowIndex, sortRow)
' Update lastValue
lastValue = currentValue
End If
Next
End If
MyBase.Render(writer)
End Sub
Ce code commence par référencer par programmation l’objet Table
trouvé à la racine de la hiérarchie de contrôle GridView et créer une variable de chaîne nommée lastValue
. lastValue
est utilisé pour comparer la valeur de colonne triée de la ligne actuelle avec la valeur de ligne précédente. Ensuite, la collection GridView est Rows
énumérée et, pour chaque ligne, la valeur de la colonne triée est stockée dans la currentValue
variable.
Notes
Pour déterminer la valeur de la colonne triée de la ligne particulière, j’utilise la propriété de Text
cellule. Cela fonctionne bien pour BoundFields, mais ne fonctionne pas comme souhaité pour TemplateFields, CheckBoxFields, etc. Nous allons voir comment prendre en compte les autres champs GridView sous peu.
Les currentValue
variables et lastValue
sont ensuite comparées. Si elles diffèrent, nous devons ajouter une nouvelle ligne de séparation à la hiérarchie de contrôle. Pour ce faire, déterminez l’index de dans GridViewRow
la Table
collection de l’objet, Rows
créez de nouvelles GridViewRow
instances et TableCell
, puis ajoutez et TableCell
GridViewRow
à la hiérarchie de contrôle.
Notez que la ligne de séparation s seule TableCell
est mise en forme de telle sorte qu’elle s’étend sur toute la largeur du GridView, qu’elle est mise en forme à l’aide de la SortHeaderRowStyle
classe CSS et qu’elle a sa Text
propriété telle qu’elle affiche à la fois le nom du groupe de tri (par exemple, Category ) et la valeur du groupe (par exemple Boissons ). Enfin, lastValue
est mis à jour avec la valeur de currentValue
.
La classe CSS utilisée pour mettre en forme la ligne SortHeaderRowStyle
d’en-tête de groupe de tri doit être spécifiée dans le Styles.css
fichier. N’hésitez pas à utiliser les paramètres de style qui vous intéressent ; J’ai utilisé les éléments suivants :
.SortHeaderRowStyle
{
background-color: #c00;
text-align: left;
font-weight: bold;
color: White;
}
Avec le code actuel, l’interface de tri ajoute des en-têtes de groupe de tri lors du tri par n’importe quel objet BoundField (voir la Figure 5, qui montre une capture d’écran lors du tri par fournisseur). Toutefois, lors du tri par n’importe quel autre type de champ (par exemple, checkBoxField ou TemplateField), les en-têtes de groupe de tri sont introuvables (voir la figure 6).
Figure 5 : L’interface de tri inclut des en-têtes de groupe de tri lors du tri par boundfields (cliquez pour afficher l’image en taille réelle)
Figure 6 : Les en-têtes de groupe de tri sont manquants lors du tri d’un champ CheckBoxField (cliquez pour afficher l’image de taille réelle)
La raison pour laquelle les en-têtes de groupe de tri sont manquants lors du tri par checkBoxField est que le code utilise actuellement uniquement la TableCell
propriété s Text
pour déterminer la valeur de la colonne triée pour chaque ligne. Pour CheckBoxFields, la TableCell
propriété s Text
est une chaîne vide ; au lieu de cela, la valeur est disponible via un contrôle Web CheckBox qui réside dans la TableCell
collection s Controls
.
Pour gérer des types de champs autres que BoundFields, nous devons augmenter le code dans lequel la currentValue
variable est affectée à case activée pour l’existence d’un contrôle CheckBox dans la TableCell
collection sControls
. Au lieu d’utiliser currentValue = gvr.Cells(sortColumnIndex).Text
, remplacez ce code par ce qui suit :
Dim currentValue As String = String.Empty
If gvr.Cells(sortColumnIndex).Controls.Count > 0 Then
If TypeOf gvr.Cells(sortColumnIndex).Controls(0) Is CheckBox Then
If CType(gvr.Cells(sortColumnIndex).Controls(0), CheckBox).Checked Then
currentValue = "Yes"
Else
currentValue = "No"
End If
' ... Add other checks here if using columns with other
' Web controls in them (Calendars, DropDownLists, etc.) ...
End If
Else
currentValue = gvr.Cells(sortColumnIndex).Text
End If
Ce code examine la colonne TableCell
triée de la ligne active pour déterminer s’il existe des contrôles dans la Controls
collection. S’il existe et que le premier contrôle est un Contrôle CheckBox, la currentValue
variable est définie sur Oui ou Non, selon la propriété CheckBox.Checked
Sinon, la valeur est extraite de la TableCell
propriété s Text
. Cette logique peut être répliquée pour gérer le tri pour tous les templateFields qui peuvent exister dans GridView.
Avec l’ajout de code ci-dessus, les en-têtes de groupe de tri sont désormais présents lors du tri par le champ CheckBoxField discontinué (voir la figure 7).
Figure 7 : Les en-têtes de groupe de tri sont maintenant présents lors du tri d’un champ CheckBoxField (cliquez pour afficher l’image de taille réelle)
Notes
Si vous avez des produits avec NULL
des valeurs de base de données pour les CategoryID
champs , SupplierID
ou UnitPrice
, ces valeurs apparaissent sous forme de chaînes vides dans gridView par défaut, ce qui signifie que le texte de ligne de séparation pour les produits avec NULL
des valeurs se lira comme Catégorie : (autrement dit, il n’y a pas de nom après Catégorie : comme avec Catégorie : Boissons ). Si vous souhaitez qu’une valeur s’affiche ici, vous pouvez définir la propriété BoundFields NullDisplayText
sur le texte que vous souhaitez afficher ou ajouter une instruction conditionnelle dans la méthode Render lors de l’affectation de à la currentValue
propriété de ligne du Text
séparateur.
Résumé
GridView n’inclut pas beaucoup d’options intégrées pour personnaliser l’interface de tri. Toutefois, avec un peu de code de bas niveau, il est possible d’ajuster la hiérarchie de contrôle de GridView pour créer une interface plus personnalisée. Dans ce tutoriel, nous avons vu comment ajouter une ligne de séparateur de groupe de tri pour un GridView triable, qui identifie plus facilement les groupes distincts et les limites de ces groupes. Pour obtenir d’autres exemples d’interfaces de tri personnalisées, case activée l’entrée de blog A Few ASP.NET 2.0 GridView Triing Tips and Tricks de Scott Guthrie.
Bonne programmation !
À propos de l’auteur
Scott Mitchell, auteur de sept livres ASP/ASP.NET et fondateur de 4GuysFromRolla.com, travaille avec les technologies Web Microsoft depuis 1998. Scott travaille comme consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Il est accessible à l’adressemitchell@4GuysFromRolla.com . ou via son blog, qui se trouve à l’adresse http://ScottOnWriting.NET.