Quando usam uma CollectionViewSource e agrupam por várias propriedades, provavelmente vão querer distinguir os cabeçalhos. Neste artigo vemos como especificar templates diferentes tendo em conta o tipo do cabeçalho.
Vamos considerar que queremos mostrar uma listagem de pessoas agrupadas por ano de nascimento e por estado civil.
A nossa classe Person
:
public class Person
{
public DateTime Birthday { get; set; }
public string Name { get; set; }
public MaritalStatus MaritalStatus { get; set; }
}
O view model:
public class IPeopleViewModel
{
public IEnumerable<Person> People { get; }
}
Definimos a nossa ListView
:
<ListView DataContext="{StaticResource PeopleGroups}" ItemsSource="{Binding}"> <ListView.GroupStyle> <StaticResourceExtension ResourceKey="GroupStyle" /> </ListView.GroupStyle> <ListView.View> <GridView> <GridViewColumn Header="Birthday" DisplayMemberBinding="{Binding Birthday, StringFormat=&
39;d MMMM&39;}" /> <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" /> </GridView> </ListView.View> </ListView>
O agrupamento e ordenação:
<CollectionViewSource x:Key="PeopleGroups" Source="{Binding People}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Birthday.Year" />
<PropertyGroupDescription PropertyName="MaritalStatus" />
</CollectionViewSource.GroupDescriptions>
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Birthday" Direction="Ascending" />
<scm:SortDescription PropertyName="Name" Direction="Ascending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
Reparem que agrupamos pela propriedade Year
do aniversário.
Não se esqueçam do namespace scm
:
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
Este será o nosso estilo:
<GroupStyle x:Key="GroupStyle">
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Name, Converter={my:TypeConverter}}" Value="{x:Type System:Int32}">
<Setter Property="Template" Value="{StaticResource BirthdayHeaderTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Name, Converter={my:TypeConverter}}" Value="{x:Type my:MaritalStatus}">
<Setter Property="Template" Value="{StaticResource MaritalStatusHeaderTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
O namespace System
:
xmlns:System="clr-namespace:System;assembly=mscorlib"
Criamos triggers de estilo tendo em conta o tipo do cabeçalho.
Dentro do estilo estamos a ligar a uma CollectionViewGroup. A propriedade Name contém o objecto com o valor do cabeçalho, por isso ligamos a esse valor.
Estamos a usar um conversor para determinar o tipo. O conversor é também uma extensão de markup. Podem ler mais sobre esta abordagem em Como usar conversores sem criar recursos estáticos.
O conversor:
[ValueConversion(typeof(object), typeof(Type))]
public class TypeConverter : ConverterMarkupExtension<TypeConverter>
{
public TypeConverter()
{
}
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
return value.GetType();
}
return null;
}
}
Agora podemos definir os diferentes templates para cada tipo do cabeçalho.
Para o cabeçalho do aniversário:
<ControlTemplate x:Key="BirthdayHeaderTemplate" TargetType="{x:Type GroupItem}">
<StackPanel Margin="0,10,0,0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding ItemCount, StringFormat={} ({0})}" />
</StackPanel>
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
Usamos a propriedade ItemCount para mostrar o número de resultados no grupo.
O cabeçalho do estado civil:
<ControlTemplate x:Key="MaritalStatusHeaderTemplate" TargetType="{x:Type GroupItem}">
<StackPanel Margin="5,10,0,0">
<TextBlock Text="{Binding Name}" FontStyle="Italic" />
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
E finalmente o resultado: