2010-11-24 9 views
6

Primero, here es la publicación anterior que trata con el enlace de datos AccountListBox ListBox a mis cuentas ObservableCollection<Account> de la clase AccountsCollection.cs.Cómo agrupar ListBoxItems por primera letra en WPF usando XAML?

así que ahora tengo un AccountsCollection objeto de vinculación y una DataTemplate llamado AccountTemplate para mi ListBox se define en los recursos:

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

y aquí está el código relacionado con el LisBox sí:

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

Quiero que mi lista esté diseñada para agrupar todas las cuentas comenzando con la letra y para mostrar esa letra en la lista (también quiero aplicar algún diseño a esa cabecera de la carta ). El resultado final debe ser algo como esto:

alt text

Gracias por toda la ayuda!

ACTUALIZACIÓN: Aquí está el código con la agrupación implementada con éxito.

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

Respuesta

13

Se puede utilizar un CollectionViewSource, y un convertidor para extraer la primera letra:

<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}}"> 
    ... 

Convertidor:

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 desea aplicar un estilo al grupo, puede utilizar la propiedad 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 agradece la respuesta. No puedo entender una línea en los recursos y es ' '. ¿A qué se refería con el espacio de nombres scm y qué es SortDescription que sigue? Gracias. – Boris

+0

El espacio de nombres XML' scm' está mapeado a 'System.ComponentModel' en WindowsBase.dll:' xmlns: scm = "clr-namespace : System.ComponentModel; assembly = WindowsBase "' –

+0

Y 'SortDescription' indica los criterios de clasificación –

2

Aquí es un ejemplo de una solución que es muy similar:

En primer lugar, tenemos que generar una mejor colección para su DataContext - he aquí un ejemplo que se puede modificar fácilmente para su - fines

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()))); 
} 

entonces sólo necesitan ItemsControls anidadas para la interfaz de usuario -

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

y obtenemos esto:

alt text

+0

Gracias por la respuesta. No tengo idea cuando se trata de LINQ; Podría explicar lo siguiente: DataContext = s.Select (n => n [0]). Distinct(). ToDictionary (l => l.ToString(), l => s.Where (w => w) .StartsWith (l.ToString()))); No quiero decir que lo conviertas en algo, te agradecería enormemente si pudieras describir en inglés lo que está pasando allí :) ¡Gracias! – Boris

+0

Todo lo que el linq está haciendo es: 1) obtener una lista de todos los caracteres de prefijo en uso ('D', 'A', 'J', 'N') 2) crear un Dictionary > donde la clave es el prefijo, y el enumerable es todas las cadenas que coinciden con el prefijo Usted podría hacer lo mismo utilizando foreach loops, etc. –

+0

Entendido, gracias. Aunque ahora entiendo completamente tu código y responde mi pregunta, prefiero la solución provista por Thomas ya que está un poco más * orientada a WPF *. Sin embargo, aplaude a Dean y feliz codificación. – Boris

Cuestiones relacionadas