Voici notre problématique du jour : avoir une listbox affichant une liste d’objets avec la nécessité de trier selon 3 critères chacun portant sur une propriété différente de nos objets.
Pour notre exemple, les objets sont de ce type:
public class UserProxy
{
public Guid ID
{ set; get; }
public string Login
{ set; get; }
public DateTime Date
{ set; get; }
public DateTime LastConnection
{ set; get; }
public string Mail { get; set; }
}
Le but est de pouvoir trier par Login, Date et LastConnection.
Mise en place de la ListBox
Rien de compliqué ici, une simple ListBox avec un DataTemplate permettant d’afficher le Login et la LastConnection:
<ListBox Grid.Row="1" x:Name="lstUsers" >
<ListBox.ItemTemplate>
<DataTemplate DataType="UserProxy">
<Grid ToolTip="{Binding Mail}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Source="user.png" Width="20" Height="20" Margin="4"/>
<TextBlock Text="{Binding Login}" FontSize="24" Grid.Column="1"
Margin="10,0" VerticalAlignment="Center"/>
<TextBlock Text="{Binding LastConnectionString}" FontSize="14"
Grid.Column="2" Margin="5,0" FontStyle="Italic" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.FocusVisualStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="White" />
</Style>
</ListBox.FocusVisualStyle>
</ListBox>
Binding de base
Partant du principe que notre liste d’utilisateurs s’appelle users, le binding sur la liste se définit donc comme suit:
lstUsers.ItemsSource = users
Ajout de la notion de triage
Pour la notion de triage nous allons faire appel à LINQ. En effet, ce dernier introduit les méthodes d’extension OrderBy et OrderByDescending qui s’appliquent aux objets implémentant l’interface IEnumerable<>.
Ainsi le code avec la gestion du tri devient:
lstUsers.ItemsSource = users.OrderBy(u=>u.Login);
On peut le voir sur le code ci-dessus, la méthode d’extension OrderBy prend donc en paramètre un délégué générique dans lequel j’ai mis une expression lamba prenant un UserProxy en paramètre et retournant une chaine de caractères.
La signature précise de OrderBy est d’ailleurs un délégué de type System.Func<TSource, TKey> c’est à dire un délégué prenant un objet de classe TSource en paramètre et retournant un objet de classe TKey (qui sera utilisé comme clef pour le tri).
Partant de ce principe et grâce aux expressions lambdas, nous pouvons définir trois variables comme suit:
readonly Func<UserProxy, object> nameKeySelector = (u=>u.Login);
readonly Func<UserProxy, object> dateKeySelector = (u=>u.Date);
readonly Func<UserProxy, object> connectionKeySelector = (u => u.LastConnection);
Grâce à ces trois variables, nous allons fournir une méthode qui saura gérer le tri ascendant et descendant et qui surtout saura prendre en charge nos expressions:
void SortDatas(Func<UserProxy, object> keySelector)
{
if (users == null)
return;
if (keySelector == null)
keySelector = previousKeySelector;
if (keySelector == previousKeySelector)
{
asc = !asc;
}
else
{
asc = true;
}
previousKeySelector = keySelector;
if (asc)
lstUsers.ItemsSource = users.OrderBy(keySelector);
else
{
lstUsers.ItemsSource = users.OrderByDescending(keySelector);
}
}
Pour que la méthode fonctionne il faut juste rajouter deux variables supplémentaires:
bool asc = true;
Func<UserProxy, object> previousKeySelector;
L’usage de cette méthode devient donc relativement simple puisqu’il suffit de l’appeler avec l’une de nos expressions pour effectuer le tri associé (et si on l’appelle plusieurs fois de suite avec la même expression, le sens de tri sera inversé):
private void dateSort_Click(object sender, RoutedEventArgs e)
{
SortDatas(dateKeySelector);
}
private void nameSort_Click(object sender, RoutedEventArgs e)
{
SortDatas(nameKeySelector);
}
private void connectionSort_Click(object sender, RoutedEventArgs e)
{
SortDatas(connectionKeySelector);
}
Et le tour est joué 
.Net, WPF, Tutorial
LINQ