Agrupación de Xamarin.Forms CollectionView

Los conjuntos de datos de gran tamaño a menudo se pueden volver inconfundibles cuando se presentan en una lista de desplazamiento continuo. En este escenario, la organización de los datos en grupos puede mejorar la experiencia del usuario al facilitar la navegación por los datos.

CollectionView admite la visualización de datos agrupados y define las siguientes propiedades que controlan cómo se presentará:

  • IsGrouped, de tipo bool, indica si los datos subyacentes deben mostrarse en grupos. El valor predeterminado de esta propiedad es false.
  • GroupHeaderTemplate, de tipo DataTemplate, la plantilla que se va a usar para el encabezado de cada grupo.
  • GroupFooterTemplate, de tipo DataTemplate, la plantilla que se va a usar para el pie de página de cada grupo.

Todas estas propiedades están respaldadas por objetos BindableProperty, lo que significa que las propiedades pueden ser destinos de los enlaces de datos.

En las capturas de pantalla siguientes se muestra un objeto CollectionView que muestra datos agrupados:

Captura de pantalla de datos agrupados en CollectionView, en iOS y Android

Para obtener más información sobre las plantillas de datos, consulte Plantillas de datos de Xamarin.Forms.

Agrupación de datos

Los datos se deben agrupar antes de que se puedan mostrar. Esto se puede lograr mediante la creación de una lista de grupos, donde cada grupo es una lista de elementos. La lista de grupos debe ser una colección de IEnumerable<T>, donde T define dos fragmentos de datos:

  • Nombre de grupo.
  • Una colección IEnumerable que define los elementos que pertenecen al grupo.

El proceso de agrupación de datos, por lo tanto, consiste en:

  • Crear un tipo que modele un solo elemento.
  • Crear un tipo que modele un único grupo de elementos.
  • Crear una colección IEnumerable<T>, donde T es el tipo que modela un único grupo de elementos. Por tanto, esta es una colección de grupos, que almacena los datos agrupados.
  • Agregar un elemento a la colección IEnumerable<T>.

Ejemplo

Al agrupar datos, el primer paso es crear un tipo que modele un solo elemento. El ejemplo siguiente muestra la clase Animal para la aplicación de ejemplo:

public class Animal
{
    public string Name { get; set; }
    public string Location { get; set; }
    public string Details { get; set; }
    public string ImageUrl { get; set; }
}

La clase Animal modela un solo elemento. Luego se puede crear un tipo que modele un grupo de elementos. El ejemplo siguiente muestra la clase AnimalGroup de la aplicación de ejemplo:

public class AnimalGroup : List<Animal>
{
    public string Name { get; private set; }

    public AnimalGroup(string name, List<Animal> animals) : base(animals)
    {
        Name = name;
    }
}

La clase AnimalGroup hereda de la clase List<T> y agrega una propiedad Name que representa el nombre del grupo.

Después se puede crear una colección de grupos IEnumerable<T>:

public List<AnimalGroup> Animals { get; private set; } = new List<AnimalGroup>();

Este código define una colección denominada Animals, donde cada elemento de la colección es un objeto AnimalGroup. Cada objeto AnimalGroup consta de un nombre y una colección List<Animal> que define los objetos del grupo Animal.

Los datos agrupados se pueden agregar a la colección Animals después:

Animals.Add(new AnimalGroup("Bears", new List<Animal>
{
    new Animal
    {
        Name = "American Black Bear",
        Location = "North America",
        Details = "Details about the bear go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/0/08/01_Schwarzbär.jpg"
    },
    new Animal
    {
        Name = "Asian Black Bear",
        Location = "Asia",
        Details = "Details about the bear go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG/180px-Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG"
    },
    // ...
}));

Animals.Add(new AnimalGroup("Monkeys", new List<Animal>
{
    new Animal
    {
        Name = "Baboon",
        Location = "Africa & Asia",
        Details = "Details about the monkey go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_anubis_%28Serengeti%2C_2009%29.jpg/200px-Papio_anubis_%28Serengeti%2C_2009%29.jpg"
    },
    new Animal
    {
        Name = "Capuchin Monkey",
        Location = "Central & South America",
        Details = "Details about the monkey go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Capuchin_Costa_Rica.jpg/200px-Capuchin_Costa_Rica.jpg"
    },
    new Animal
    {
        Name = "Blue Monkey",
        Location = "Central and East Africa",
        Details = "Details about the monkey go here.",
        ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/BlueMonkey.jpg/220px-BlueMonkey.jpg"
    },
    // ...
}));

Este código crea dos grupos en la colección Animals. El primer AnimalGroup se denomina Bears y contiene una colección List<Animal> de detalles de osos. El segundo AnimalGroup se llama Monkeys y contiene una colección List<Animal> de detalles de mono.

Visualización de datos agrupados

CollectionView mostrará los datos agrupados, siempre que los datos se hayan agrupado correctamente, estableciendo la propiedad IsGrouped en true:

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                ...
                <Image Grid.RowSpan="2"
                       Source="{Binding ImageUrl}"
                       Aspect="AspectFill"
                       HeightRequest="60"
                       WidthRequest="60" />
                <Label Grid.Column="1"
                       Text="{Binding Name}"
                       FontAttributes="Bold" />
                <Label Grid.Row="1"
                       Grid.Column="1"
                       Text="{Binding Location}"
                       FontAttributes="Italic"
                       VerticalOptions="End" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

El código de C# equivalente es el siguiente:

CollectionView collectionView = new CollectionView
{
    IsGrouped = true
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Animals");
// ...

La apariencia de cada elemento en CollectionView se define estableciendo la propiedad CollectionView.ItemTemplate en DataTemplate. Para obtener más información, consulta Definir la apariencia del elemento.

Nota:

De forma predeterminada, CollectionView mostrará el nombre del grupo en el encabezado y pie de página del grupo. Este comportamiento se puede cambiar personalizando el encabezado de grupo y el pie de página de grupo.

Personalización del encabezado de grupo

Se puede personalizar la apariencia de cada encabezado de grupo mediante el establecimiento de la propiedad CollectionView.GroupHeaderTemplate en DataTemplate:

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true">
    ...
    <CollectionView.GroupHeaderTemplate>
        <DataTemplate>
            <Label Text="{Binding Name}"
                   BackgroundColor="LightGray"
                   FontSize="Large"
                   FontAttributes="Bold" />
        </DataTemplate>
    </CollectionView.GroupHeaderTemplate>
</CollectionView>

En este ejemplo, cada encabezado de grupo se establece en una Label que muestra el nombre del grupo y que tiene otras propiedades de apariencia establecidas. En las capturas de pantalla siguientes se muestra el encabezado de grupo personalizado:

Captura de pantalla de un encabezado de grupo personalizado en CollectionView, en iOS y Android

Se puede personalizar la apariencia de cada pie de página del grupo estableciendo la propiedad CollectionView.GroupFooterTemplate en DataTemplate:

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true">
    ...
    <CollectionView.GroupFooterTemplate>
        <DataTemplate>
            <Label Text="{Binding Count, StringFormat='Total animals: {0:D}'}"
                   Margin="0,0,0,10" />
        </DataTemplate>
    </CollectionView.GroupFooterTemplate>
</CollectionView>

En este ejemplo, cada pie de página del grupo se establece en un objeto Label que muestra el número de elementos del grupo. En las capturas de pantalla siguientes se muestra el pie de página de grupo personalizado:

Captura de pantalla de un pie de página de grupo personalizado en CollectionView, en iOS y Android

Grupos vacíos

Cuando CollectionView muestra datos agrupados, se mostrarán todos los grupos que estén vacíos. Estos grupos se mostrarán con un encabezado de grupo y un pie de página, lo que indica que el grupo está vacío. En las capturas de pantalla siguientes se muestra un grupo vacío:

Captura de pantalla de un grupo vacío en CollectionView, en iOS y Android

Nota:

En iOS 10 y versiones inferiores, los encabezados y pies de página de grupo para grupos vacíos se pueden mostrar en la parte superior de CollectionView.

Agrupar sin plantillas

CollectionView puede mostrar datos agrupados correctamente sin establecer la propiedad CollectionView.ItemTemplate en una DataTemplate:

<CollectionView ItemsSource="{Binding Animals}"
                IsGrouped="true" />

En este escenario, se pueden mostrar datos significativos reemplazando el método ToString en el tipo que modela un solo elemento y el tipo que modela un único grupo de elementos.