2010-11-24 51 views
6

Tout d'abord, here est l'article précédent qui traite de la liaison de données AccountListBox ListBox à mes comptes ObservableCollection<Account> de la classe AccountsCollection.cs.Comment grouper ListBoxItems par première lettre dans WPF en utilisant XAML?

Alors maintenant, j'ai un objet de liaison AccountsCollection et un DataTemplate nommé AccountTemplate pour mon ListBox défini dans les ressources:

<Window.Resources> 
    <controller:AccountsWindowController x:Key="AccountsCollection" /> 
    <DataTemplate x:Key="AccountTemplate"> 
     <DockPanel> 
      <Button Name="EditButton" 
        DockPanel.Dock="Right" 
        Margin="3 0 3 0" 
        VerticalAlignment="Center" 
        Content="Edit" /> 
      <Button Name="DeleteButton" 
        DockPanel.Dock="Right" 
        Margin="3 0 3 0" 
        VerticalAlignment="Center" 
        Content="Delete" /> 
      <TextBlock Name="AccountName" 
         VerticalAlignment="Center" 
         Text="{Binding Name}" 
         TextWrapping="NoWrap" 
         TextTrimming="CharacterEllipsis" /> 
     </DockPanel> 
    </DataTemplate> 
<Window.Resources> 

Et est le code ici lié à la LisBox lui-même:

<ListBox Name="AccountsListBox" 
     Margin="12,38,12,41" 
     HorizontalContentAlignment="Stretch" 
     ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
     ItemsSource="{Binding Accounts, 
      Source={StaticResource ResourceKey=AccountsCollection}}" 
     ItemTemplate="{StaticResource ResourceKey=AccountTemplate}" 
     MouseDoubleClick="AccountsListBox_MouseDoubleClick"> 
</ListBox> 

Je veux que ma liste soit conçue pour grouper tous les comptes en commençant la lettre et montrer cette lettre dans la liste (aussi je veux appliquer un certain dessin à cet en-tête de lettre). Le résultat final devrait être quelque chose comme ceci:

alt text

Merci pour toute l'aide!

MISE À JOUR: Voici le code avec le regroupement implémenté avec succès.

<Window x:Class="Gui.Wpf.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:entities="clr-namespace:Entities.Accounts;assembly=Entities" 
    xmlns:contollers="clr-namespace:Gui.Wpf.Controllers" 
    xmlns:converters="clr-namespace:Gui.Wpf.Converters" 
    xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
    Title="MainWindow" 
    Width="525" 
    Height="350" > 

<Window.Resources> 

    <!-- Main window controller --> 
    <contollers:MainWindowController 
     x:Key="MainWindowController" /> 

    <!-- Converter for first letter extraction from the account name --> 
    <converters:FirstLetterConverter x:Key="FirstLetterConv" /> 

    <!-- View source for the AccountsListBox --> 
    <CollectionViewSource 
     x:Key="AccountsView" 
     Source="{Binding Accounts, Source={StaticResource ResourceKey=MainWindowController}}"> 

     <!-- Sorting --> 
     <CollectionViewSource.SortDescriptions> 
      <componentModel:SortDescription PropertyName="AccountName" /> 
     </CollectionViewSource.SortDescriptions> 

     <!-- Grouping --> 
     <CollectionViewSource.GroupDescriptions> 
      <PropertyGroupDescription PropertyName="AccountName" Converter="{StaticResource ResourceKey=FirstLetterConv}" /> 
     </CollectionViewSource.GroupDescriptions> 

    </CollectionViewSource> 

    <!-- Data template for the type Account --> 
    <DataTemplate 
     DataType="{x:Type entities:Account}"> 
     <DockPanel> 
      <Button 
       Name="DeleteButton" 
       DockPanel.Dock="Right" 
       Margin="3, 1, 3, 1" 
       VerticalAlignment="Center" 
       Content="Delete" /> 
      <Button 
       Name="EditButton" 
       DockPanel.Dock="Right" 
       Margin="3, 1, 3, 1" 
       VerticalAlignment="Center" 
       Content="Edit" /> 
      <TextBlock 
       Name="AccountNameTextBlock" 
       VerticalAlignment="Center" 
       Text="{Binding AccountName}" 
       TextWrapping="NoWrap" 
       TextTrimming="CharacterEllipsis" /> 
     </DockPanel> 

    </DataTemplate> 

    <!-- Data template for AccountListBox grouping --> 
    <DataTemplate x:Key="GroupingHeader"> 
     <TextBlock Text="{Binding Path=Name}" Background="Black" Foreground="White" /> 
    </DataTemplate> 

</Window.Resources> 

<Grid> 
    <ListBox 
     Name="AccountsListBox" 
     Width="300" 
     Height="200" 
     HorizontalAlignment="Center" 
     VerticalAlignment="Center" 
     ItemsSource="{Binding Source={StaticResource ResourceKey=AccountsView}}" 
     HorizontalContentAlignment="Stretch" > 
     <ListBox.GroupStyle> 
      <GroupStyle 
       HeaderTemplate="{StaticResource ResourceKey=GroupingHeader}" /> 
     </ListBox.GroupStyle> 
    </ListBox> 
</Grid> 

Répondre

13

Vous pouvez utiliser un CollectionViewSource et un convertisseur pour extraire la première lettre:

<local:FirstLetterConverter x:Key="firstLetterConverter" /> 

<CollectionViewSource x:Key="cvs" Source="{Binding Accounts, Source={StaticResource AccountsCollection}}"> 
    <CollectionViewSource.SortDescriptions> 
     <scm:SortDescription PropertyName="AccountName" /> 
    </CollectionViewSource.SortDescriptions> 
    <CollectionViewSource.GroupDescriptions> 
     <PropertyGroupDescription PropertyName="AccountName" Converter="{StaticResource firstLetterConverter}" /> 
    </CollectionViewSource.GroupDescriptions> 
</CollectionViewSource> 

... 

<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}"> 
    ... 

Converter:

public class FirstLetterConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     string s = value as string; 
     if (s != null && s.Length > 0) 
      return s.Substring(0, 1); 
     return string.Empty; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 
} 

Si vous voulez appliquer un style au groupe, vous pouvez utiliser la propriété GroupStyle:

... 
    <ItemsControl.GroupStyle> 
    <GroupStyle> 
     <GroupStyle.HeaderTemplate> 
     <DataTemplate> 
      <TextBlock FontWeight="Bold" FontSize="15" Text="{Binding Path=Name}" /> 
     </DataTemplate> 
     </GroupStyle.HeaderTemplate> 
     <GroupStyle.ContainerStyle> 
     <Style TargetType="{x:Type GroupItem}"> 
      <Setter Property="Background" Value="Gray" /> 
      <Setter Property="Foreground" Value="White" /> 
     </Style> 
     </GroupStyle.ContainerStyle> 
    </GroupStyle> 
    </ItemsControl.GroupStyle> 
    ... 
+0

Thomas merci pour la réponse, je suis incapable de comprendre une ligne dans les ressources et c'est ' 'A quoi faisiez-vous référence avec l'espace de nom scm et qu'est-ce que SortDescription qui suit? – Boris

+0

L'espace de noms XML' scm' est mappé à 'System.ComponentModel' dans WindowsBase.dll:' xmlns: scm = "clr-namespace : System.ComponentModel; assembly = WindowsBase "' –

+0

Et 'SortDescription' indique les critères de tri –

2

Voici un exemple d'une solution qui est très similaire:

Tout d'abord, nous avons besoin de générer une meilleure collection pour votre DataContext - est ici un exemple que vous pouvez facilement modifier pour votre fins -

public Window1() 
{ 
    InitializeComponent(); 
    var s = new[] { "Dave", "Adam", "Jeny", "Nick", "James" }; 
    DataContext = s 
     .Select(n => n[0]) 
     .Distinct() 
     .ToDictionary(l => l.ToString(), l => s 
      .Where(w => w 
       .StartsWith(l.ToString()))); 
} 

alors nous avons juste besoin ItemsControls imbriqués pour l'interface utilisateur -

<ItemsControl ItemsSource="{Binding}"> 
<ItemsControl.ItemTemplate> 
    <DataTemplate> 
    <StackPanel> 
     <TextBlock Foreground="Red" Text="{Binding Key}" FontSize="12" Margin="5" /> 
     <ItemsControl ItemsSource="{Binding Value}"> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
      <StackPanel Orientation="Horizontal" Margin="5"> 
       <Button Content ="View" Margin="0,0,5,0" /> 
       <Button Content ="Delete" Margin="0,0,5,0" /> 
       <TextBlock Text="{Binding}" /> 
      </StackPanel> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </StackPanel> 
    </DataTemplate> 
</ItemsControl.ItemTemplate> 

et nous obtenons ceci:

alt text

+0

Merci pour la réponse. Je suis désemparé quand il s'agit de LINQ; Pourriez-vous simplement expliquer ce qui suit: DataContext = s.Select (n => n [0]). Distinct(). ToDictionary (l => l.ToString(), l => s.Where (w => w .StartsWith (l.ToString()))); Je ne veux pas dire que vous le convertissez en quelque chose, j'apprécierais grandement si vous pouviez décrire en anglais clair ce qui se passe là-bas :) Merci! – Boris

+0

Tout ce que le linq fait est: 1) obtenir une liste de tous les caractères de préfixe utilisés ('D', 'A', 'J', 'N') 2) créer un Dictionary > où la clé est le préfixe, et l'énumérable est toutes les chaînes qui correspondent au préfixe Vous pourriez faire la même chose en utilisant foreach loops etc –

+0

Got it, thanks. Bien que je comprends parfaitement votre code maintenant et qu'il réponde à ma question, je préfère la solution fournie par Thomas car elle est un peu plus orientée WPF *. Néanmoins, acclamations Dean et codage heureux. – Boris