This article has been localized into French by the community.
How-To: ListView avec tri par colonne
Dans le chapitre précédent, nous avions vu comment il était possible de trier facilement une ListView à partir du code C#. Bien que ce soit suffisant dans la plupart des cas, l'utilisateur ne peut pas trier la liste. En plus de cela, il n'y avait aucune indication de la colonne utilisée pour effectuer le tri. Dans Windows, et dans beaucoup d'interfaces utilisateurs en général, il est commun d'illustrer l'ordre de tri en dessinant un triangle à côté du nom de la colonne utilisée pour le tri.
Dans cet article How-To , je vais vous montrer une solution pratique qui permet de faire tout ce que nous avons cités précédemment. Cependant, gardez à l'esprit qu'une partie du code de cet exemple utilise des notions que vous n'avez pas encore apprises - c'est pourquoi cet article a le libellé How-To.
Cet article est basé sur le précédent mais je réepliquerai chaque partie au fur et à mesure de l'avancement. Voici notre but - une ListView avec un tri par colonne, incluant une information visuelle du sens de tri effectué et sur quel champ le tri est effectué. L'utilisateur va simplement cliquer sur une colonne pour trier les données et si la même colonne est cliquée plusieurs fois, le sens de tri est inversé. Voici à quoi ressemble le code:
Le code XAML
La première chose dont nous avons besoin est du code XAMLpour définir notre interface utilisateur. Le voici:
<Window x:Class="WpfTutorialSamples.ListView_control.ListViewColumnSortingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListViewColumnSortingSample" Height="200" Width="350">
<Grid Margin="10">
<ListView Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Width="120" DisplayMemberBinding="{Binding Name}">
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Name" Click="lvUsersColumnHeader_Click">Name</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="80" DisplayMemberBinding="{Binding Age}">
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Age" Click="lvUsersColumnHeader_Click">Age</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="80" DisplayMemberBinding="{Binding Sex}">
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Sex" Click="lvUsersColumnHeader_Click">Sex</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Remarquez la façon dont sont définis les entêtes des colonnes. Un élément GridViewColumnHeader est défini au lieu d'une chaîne de caractères. Cela permet d'ajouter d'autre propriétés à l'entête : dans notre exemple la propriété Tag ainsi que l'événement Click.
La propriété Tag est utilisée pour récupérer le champ qui va être utilisé pour trier la ListView si cette colonne en particulier est cliquée. Pour cela, chaque colonne a souscrit à l'événement lvUsersColumnHeader_Click.
Voici les concepts clés du code XAML. A côté de ça, nous avons associé les propriétés Name, Age et Sex au code C# relatif au code XAML, ce que nous allons voir maintenant.
Le code C#
Dans le code C# apparaissent quelques nouveautés. J'utilise au total 3 classes, que vous devriez normalement séparées dans 3 fichiers individuels, mais par soucis de commodité, tout le code est dans le même fichier pour un total d'une centaine de lignes à peu près. Premièrement le code et je vous explique ensuite son fonctionnement:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
namespace WpfTutorialSamples.ListView_control
{
public partial class ListViewColumnSortingSample : Window
{
private GridViewColumnHeader listViewSortCol = null;
private SortAdorner listViewSortAdorner = null;
public ListViewColumnSortingSample()
{
InitializeComponent();
List<User> items = new List<User>();
items.Add(new User() { Name = "John Doe", Age = 42, Sex = SexType.Male });
items.Add(new User() { Name = "Jane Doe", Age = 39, Sex = SexType.Female });
items.Add(new User() { Name = "Sammy Doe", Age = 13, Sex = SexType.Male });
items.Add(new User() { Name = "Donna Doe", Age = 13, Sex = SexType.Female });
lvUsers.ItemsSource = items;
}
private void lvUsersColumnHeader_Click(object sender, RoutedEventArgs e)
{
GridViewColumnHeader column = (sender as GridViewColumnHeader);
string sortBy = column.Tag.ToString();
if(listViewSortCol != null)
{
AdornerLayer.GetAdornerLayer(listViewSortCol).Remove(listViewSortAdorner);
lvUsers.Items.SortDescriptions.Clear();
}
ListSortDirection newDir = ListSortDirection.Ascending;
if(listViewSortCol == column && listViewSortAdorner.Direction == newDir)
newDir = ListSortDirection.Descending;
listViewSortCol = column;
listViewSortAdorner = new SortAdorner(listViewSortCol, newDir);
AdornerLayer.GetAdornerLayer(listViewSortCol).Add(listViewSortAdorner);
lvUsers.Items.SortDescriptions.Add(new SortDescription(sortBy, newDir));
}
}
public enum SexType { Male, Female };
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string Mail { get; set; }
public SexType Sex { get; set; }
}
public class SortAdorner : Adorner
{
private static Geometry ascGeometry =
Geometry.Parse("M 0 4 L 3.5 0 L 7 4 Z");
private static Geometry descGeometry =
Geometry.Parse("M 0 0 L 3.5 4 L 7 0 Z");
public ListSortDirection Direction { get; private set; }
public SortAdorner(UIElement element, ListSortDirection dir)
: base(element)
{
this.Direction = dir;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if(AdornedElement.RenderSize.Width < 20)
return;
TranslateTransform transform = new TranslateTransform
(
AdornedElement.RenderSize.Width - 15,
(AdornedElement.RenderSize.Height - 5) / 2
);
drawingContext.PushTransform(transform);
Geometry geometry = ascGeometry;
if(this.Direction == ListSortDirection.Descending)
geometry = descGeometry;
drawingContext.DrawGeometry(Brushes.Black, null, geometry);
drawingContext.Pop();
}
}
}
Permettez-moi de commencer par le bas, puis de remonter tout en expliquant ce qui se passe. La dernière classe du fichier est une classe Adorner appelée SortAdorner . Tout ce que cette petite classe fait est de dessiner un triangle, pointant vers le haut ou vers le bas, selon la direction du tri. WPF utilise le concept d'ornements pour vous permettre de peindre des choses sur d'autres contrôles, et c'est exactement ce que nous voulons ici: La possibilité de dessiner un triangle de tri au-dessus de notre en-tête de colonne ListView.
Le SortAdorner fonctionne en définissant deux objets Geometry , qui sont essentiellement utilisés pour décrire des formes 2D - dans ce cas, un triangle avec la pointe pointant vers le haut et un avec la pointe pointant vers le bas. La méthode Geometry.Parse () utilise la liste des points pour dessiner les triangles, qui seront expliqués plus en détail dans un article ultérieur.
Le SortAdorner est conscient de la direction du tri, car il doit dessiner le triangle approprié, mais il n'est pas au courant du champ par lequel nous classons - cela est géré dans la couche UI.
La classe User est juste une classe d'informations de base, utilisée pour contenir des informations sur un utilisateur. Certaines de ces informations sont utilisées dans la couche d'interface utilisateur, que nous lions aux propriétés Name, Age et Sex.
Dans la classe Window, nous avons deux méthodes: le constructeur où nous construisons une liste d'utilisateurs et l'affectons au ItemsSource de notre ListView, puis le gestionnaire d'événements de clic qui sera appelé lorsque l'utilisateur clique sur une colonne. En haut de la classe, nous avons défini deux variables privées: listViewSortCol et listViewSortAdorner . Ceux-ci nous aideront à garder une trace de la colonne sur laquelle nous sommes en train de trier et l'ornement que nous avons placé pour l'indiquer.
Dans le gestionnaire d'événements lvUsersColumnHeader_Click, nous commençons par obtenir une référence à la colonne sur laquelle l'utilisateur a cliqué. Avec cela, nous pouvons décider de la propriété propriété de la classe User a utiliser pour trier, simplement en regardant la propriété Tag que nous avons définie dans XAML. Nous vérifions ensuite si nous trions déjà par une colonne - si c'est le cas, nous supprimons l'ornement et effaçons les descriptions de tri actuelles.
Après cela, nous sommes prêt à décider de la direction. Par défaut elle est ascendante, mais nous faisons une vérification pour voir si nous sommes déjà entrain de faire un tri par la colonne que l'utilisateur a cliqué - si c'est le cas, nous changeons la direction à descendante.
À la fin, nous créons un nouveau Ornement de Tri (SortAdorner), en passant la colonne avec laquelle il devrait être fourni, aussi bien que la direction. Nous ajoutons ceci à l'Étage d'ornement (AdornerLayer) de l'entête de colonne, et à la toute fin, nous ajoutons la Description de tri (SortDescription) à l'affichage de liste (ListView), pour lui laisser savoir par quelle propriété trier et dans quelle direction.
Résumé
Félicitations, vous avez maintenant une Vue de liste (ListView) complètement triable avec indication visuelle de tri de colonne et la direction. Au cas où vous voulez en savoir plus sur quelques uns des concepts utilisés dans cet article, comme la liaison des données, la géométrie ou les Vues de liste (ListViews) en général, alors s.v.p. jetez un coup d'oeil quelques uns des autres articles, où chacun des sujets est couvert en profondeur.