Partager via


Création d’une interface utilisateur de tri personnalisée (VB)

par Scott Mitchell

Télécharger le PDF

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.

Chaque groupe de catégories est clairement identifié

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 ProductListet 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 ProductNameCategoryName, , SupplierNameet 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.

Les données de GridView triable sont triées par catégorie

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.

L’une des techniques consiste à ajouter des lignes séparateurs à la source de données

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 :

  1. Récupérer par programmation les données à lier à GridView (un ProductsDataTable instance)
  2. Trier les données en fonction des propriétés et SortDirection des propriétés GridView SortExpression
  3. Itérer à travers dans ProductsRows le ProductsDataTable, en recherchant où se trouvent les différences dans la colonne triée
  4. À chaque limite de groupe, injectez un enregistrement ProductsRow séparateur instance dans le DataTable, celui dont il est CategoryID défini -1 sur (ou toute désignation a été décidée pour marquer un enregistrement en tant qu’enregistrement séparateur)
  5. 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.

Une autre technique manipule la hiérarchie de contrôles GridView

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 TableCellGridViewRow à 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).

L’interface de tri inclut des en-têtes de groupe de tri lors du tri par BoundFields

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)

Les en-têtes de groupe de tri sont manquants lors du tri d’un champ CheckBoxField

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

Les en-têtes de groupe de tri sont maintenant présents lors du tri d’un champ CheckBoxField

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 CategoryIDchamps , SupplierIDou 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.