Compartir a través de


ListView

Examinar ejemplo. Examinar el ejemplo

ListView de .NET Multi-platform App UI (.NET MAUI) muestra una lista vertical desplazable de elementos de datos seleccionables. Aunque ListView administra la apariencia de la lista, la apariencia de cada elemento de la lista se define mediante un DataTemplate que usa Cell para mostrar elementos. .NET MAUI incluye tipos de celda para mostrar combinaciones de texto e imágenes, y también puede definir celdas personalizadas que muestren cualquier contenido que desees. ListView también incluye compatibilidad para mostrar encabezados y pies de página, datos agrupados, elementos de menú contextual y de extracción para actualizar.

El clase ListView se deriva de la clase ItemsView<Cell>, de la que hereda las propiedades siguientes:

  • ItemsSource, de tipo IEnumerable, especifica la colección de elementos que se mostrarán en el área de sugerencias, y tiene un valor predeterminado de null.
  • ItemTemplate, de tipo DataTemplate, especifica la plantilla que se aplicará a cada elemento de la colección de elementos que se mostrará.

ListView define las siguientes propiedades:

  • Footer, de tipo object, especifica la cadena o vista que se mostrará al final de la lista.
  • FooterTemplate, de tipo DataTemplate, especifica el DataTemplate que se va a usar para dar formato a Footer.
  • GroupHeaderTemplate, de tipo DataTemplate, define el DataTemplate utilizado para definir la apariencia del encabezado de cada grupo. Esta propiedad es mutuamente excluyente con la propiedad GroupDisplayBinding. Por lo tanto, configurar esta propiedad establecerá GroupDisplayBinding en null.
  • HasUnevenRows, de tipo bool, indica si los elementos de la lista pueden tener filas de diferentes alturas. El valor predeterminado de esta propiedad es false.
  • Header, de tipo object, especifica la cadena o vista que se mostrará al principio de la lista.
  • HeaderTemplate, de tipo DataTemplate, especifica el DataTemplate que se va a usar para dar formato a Header.
  • HorizontalScrollBarVisibility, de tipo ScrollBarVisibility, indica cuándo estará visible la barra de desplazamiento horizontal.
  • IsGroupingEnabled, de tipo bool, indica si los datos subyacentes deben mostrarse en grupos. El valor predeterminado de esta propiedad es false.
  • IsPullToRefreshEnabled, de tipo bool, indica si el usuario puede deslizar el dedo hacia abajo para hacer que ListView actualice sus datos. El valor predeterminado de esta propiedad es false.
  • IsRefreshing, de tipo bool, indica si el objeto ListView se está actualizando actualmente. El valor predeterminado de esta propiedad es false.
  • RefreshCommand, de tipo ICommand, representa el comando que se ejecutará cuando se desencadene una actualización.
  • RefreshControlColor, de tipo Color, determina el color de la visualización de actualización que se muestra mientras se produce una actualización.
  • RowHeight, de tipo int, determina el alto de cada fila cuando HasUnevenRows es false.
  • SelectedItem, de tipo object, representa el elemento seleccionado actualmente en ListView.
  • SelectionMode, de tipo ListViewSelectionMode, indica si los elementos se pueden seleccionar en ListView o no. El valor predeterminado de esta propiedad es Single.
  • SeparatorColor, de tipo Color, define el color de la barra que separa los elementos de la lista.
  • SeparatorVisibility, de tipo SeparatorVisibility, define si los separadores son visibles entre los elementos.
  • VerticalScrollBarVisibility, de tipo ScrollBarVisibility, indica cuándo estará visible la barra de desplazamiento vertical.

Estas propiedades están respaldadas por objetos BindableProperty, lo que significa que pueden ser destinos de los enlaces de datos y recibir formato de estilo.

Además, ListView define las siguientes propiedades que no están respaldadas por objetos BindableProperty:

  • GroupDisplayBinding, de tipo BindingBase, establece el enlace que se usará para mostrar el encabezado de grupo. Esta propiedad es mutuamente excluyente con la propiedad GroupHeaderTemplate. Por lo tanto, configurar esta propiedad establecerá GroupHeaderTemplate en null.
  • GroupShortNameBinding, de tipo BindingBase, establece el enlace del nombre que se va a mostrar en listas de accesos directos agrupadas.
  • CachingStrategy, de tipo ListViewCachingStrategy, define la estrategia de reutilización de celdas de ListView. Se trata de una propiedad de solo lectura.

ListView define los siguientes eventos:

  • ItemAppearing, que se produce cuando la representación visual de un elemento se agrega al diseño visual de ListView. El objeto ItemVisibilityEventArgs que acompaña al evento define las propiedades Item y Index.
  • ItemDisappearing, que se produce cuando la representación visual de un elemento se quita del diseño visual de ListView. El objeto ItemVisibilityEventArgs que acompaña al evento define las propiedades Item y Index.
  • ItemSelected, que se genera cuando se selecciona un nuevo elemento de la lista. El objeto SelectedItemChangedEventArgs que acompaña al evento define las propiedades SelectedItem y SelectedItemIndex.
  • ItemTapped, que se genera cuando se pulsa un elemento en ListView. El objeto ItemTappedEventArgs que acompaña al evento define las siguientes propiedades: Group, Item y ItemIndex.
  • Refreshing, que se genera cuando se desencadena una operación de extracción para actualizar en ListView.
  • Scrolled, . El objeto ScrolledEventArgs que acompaña al evento define las propiedades ScrollX y ScrollY.
  • ScrollToRequested . El objeto ScrollToRequestedEventArgs que acompaña a este evento define las propiedades Element, Mode, Position, ScrollX, ScrollY y ShouldAnimate.

Relleno de ListView con datos

ListView se rellena con datos estableciendo su ItemsSource propiedad en cualquier colección que implemente IEnumerable.

Importante

Si es necesario que ListView se actualice a medida que se agregan, quitan o cambian elementos en la colección subyacente, la colección subyacente debe ser una IEnumerable colección que envíe notificaciones de cambio de propiedad, como ObservableCollection.

ListView se puede rellenar con datos mediante el enlace de datos para enlazar su propiedad ItemsSource a una colección IEnumerable. En XAML, esto se consigue mediante la extensión de marcado Binding.

<ListView ItemsSource="{Binding Monkeys}" />

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

ListView listView = new ListView();
listView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

En este ejemplo, los ItemsSource datos de propiedad se enlazan a la Monkeys propiedad del modelo de vista conectado.

Nota:

Los enlaces compilados se pueden habilitar para mejorar el rendimiento del enlace de datos en aplicaciones MAUI de .NET. Para obtener más información, consulta Enlaces compilados.

Para obtener más información sobre el enlace de datos, consulta Enlace de datos.

Definición de la apariencia de elementos

Se puede definir la apariencia de cada elemento de ListView mediante el establecimiento de la propiedad ItemTemplate en un DataTemplate:

<ListView ItemsSource="{Binding Monkeys}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Grid Padding="10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <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>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Los elementos especificados en el DataTemplate define la apariencia de cada elemento de la lista y el elemento secundario del DataTemplate debe ser un objeto Cell. En este ejemplo, el diseño dentro del DataTemplate se administra mediante un objeto Grid. La Grid contiene un objeto Image y dos objetos Label, que se enlazan a las propiedades de la clase Monkey:

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

Las capturas de pantalla siguientes muestran el resultado de crear plantillas para cada elemento de la lista:

Recorte de pantalla de ListView en el que se crea una plantilla para cada elemento.

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

Celdas

La apariencia de cada elemento de un objeto ListView se define mediante DataTemplate y DataTemplate debe hacer referencia a una clase Cell para mostrar elementos. Cada celda representa un elemento de datos en ListView. .NET MAUI incluye las siguientes celdas integradas:

  • TextCell, que muestra el texto principal y secundario en líneas independientes.
  • ImageCell, que muestra una imagen con texto principal y secundario en líneas independientes.
  • SwitchCell, que muestra texto y un conmutador que se pueden activar o desactivar.
  • EntryCell, que muestra una etiqueta y un texto que se pueden editar.
  • ViewCell, que es una celda personalizada cuya apariencia se define mediante unaView. Este tipo de celda debe usarse cuando se quiera definir completamente la apariencia de cada elemento de una ListView.

Normalmente, SwitchCell y EntryCell solo se usarán en TableView y no en ListView. Para obtener más información sobre SwitchCell y EntryCell, consulta TableView.

Celda de texto

Una TextCell muestra texto principal y secundario en líneas independientes. TextCell define las siguientes propiedades:

  • Text, de tipo string, define el texto principal que se va a mostrar.
  • TextColor, de tipo Color, representa el color del texto principal.
  • Detail, de tipo string, define el texto secundario que se va a mostrar.
  • DetailColor, de tipo Color, indica el color del texto secundario.
  • Command, de tipo ICommand, define el comando que se ejecuta cuando se pulsa la celda.
  • CommandParameter, de tipo object, representa el parámetro que se pasa al comando.

Estas propiedades están respaldadas por objetos BindableProperty, lo que significa que pueden ser destinos de los enlaces de datos, y que se les puede aplicar un estilo.

En el ejemplo siguiente se muestra cómo usar una TextCell para definir la apariencia de los elementos de una ListView:

<ListView ItemsSource="{Binding Food}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Name}"
                      Detail="{Binding Description}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

En la captura de pantalla siguiente se muestra la apariencia resultante de la celda:

Recorte de pantalla de ListView en el que cada elemento es una TextCell.

Celda de imagen

ImageCell muestra una imagen con texto principal y secundario en líneas independientes. ImageCell hereda las propiedades de TextCell y define la propiedad ImageSource, de tipo ImageSource, que especifica la imagen que se va a mostrar en la celda. Esta propiedad está respaldada por un objeto BindableProperty, lo que significa que puede ser destino de los enlaces de datos, y recibir formato de estilo.

En el ejemplo siguiente se muestra cómo usar la apariencia de un ImageCell para definir el aspecto de los elementos mediante ListView:

<ListView ItemsSource="{Binding Food}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ImageCell ImageSource="{Binding Image}"
                       Text="{Binding Name}"
                       Detail="{Binding Description}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

En la captura de pantalla siguiente se muestra la apariencia resultante de la celda:

Recorte de pantalla de ListView en el que cada elemento es una ImageCell.

Celda de vista

ViewCell es una celda personalizada cuya apariencia se define mediante View. ViewCell define una propiedad View, de tipo View, que define la vista que representa el contenido de la celda. Esta propiedad está respaldada por un objeto BindableProperty, lo que significa que puede ser destino de los enlaces de datos, y recibir formato de estilo.

Nota:

La propiedad View es la propiedad de contenido de la clase ViewCell y, por tanto, no es necesario establecerla explícitamente desde XAML.

En el ejemplo siguiente se muestra cómo usar una ViewCell para definir la apariencia de los elementos de una ListView:

<ListView ItemsSource="{Binding Monkeys}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Grid Padding="10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <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>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Dentro de ViewCell, el diseño se puede administrar mediante cualquier diseño de .NET MAUI. En este ejemplo, el diseño se administra por un Grid. El Grid contiene un objeto Image y dos objetos Label, que se enlazan a las propiedades de la clase Monkey.

Las capturas de pantalla siguientes muestran el resultado de crear plantillas para cada elemento de la lista:

Recorte de pantalla de ListView en el que se crea una plantilla para cada elemento con una ViewCell.

Elección de la apariencia del elemento en tiempo de ejecución

La apariencia de cada elemento de ListView se puede elegir a la hora de ejecutarlo, en función del valor del elemento, estableciendo la propiedad ItemTemplate en un objeto DataTemplateSelector:

<ContentPage ...
             xmlns:templates="clr-namespace:ListViewDemos.Templates">
    <ContentPage.Resources>
        <DataTemplate x:Key="AmericanMonkeyTemplate">
            <ViewCell>
                ...
            </ViewCell>
        </DataTemplate>

        <DataTemplate x:Key="OtherMonkeyTemplate">
            <ViewCell>
                ...
            </ViewCell>
        </DataTemplate>

        <templates:MonkeyDataTemplateSelector x:Key="MonkeySelector"
                                             AmericanMonkey="{StaticResource AmericanMonkeyTemplate}"
                                             OtherMonkey="{StaticResource OtherMonkeyTemplate}" />
    </ContentPage.Resources>

    <Grid Margin="20">
        <ListView ItemsSource="{Binding Monkeys}"
                  ItemTemplate="{StaticResource MonkeySelector}" />
    </Grid>
</ContentPage>

La propiedad ItemTemplate se establece en el objeto MonkeyDataTemplateSelector. En el ejemplo siguiente se muestra la clase MonkeyDataTemplateSelector:

public class MonkeyDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate AmericanMonkey { get; set; }
    public DataTemplate OtherMonkey { get; set; }

    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        return ((Monkey)item).Location.Contains("America") ? AmericanMonkey : OtherMonkey;
    }
}

La clase MonkeyDataTemplateSelector define propiedades DataTemplate AmericanMonkey y OtherMonkey que se establecen en diferentes plantillas de datos. La invalidación de OnSelectTemplate devuelve la AmericanMonkey plantilla, que muestra el nombre y la ubicación del mono en verde azulado, cuando el nombre del mono contiene "America". Cuando el nombre del mono no contiene "America", la invalidación de OnSelectTemplate devuelve la plantilla OtherMonkey, que muestra el nombre del mono y la ubicación en plata:

Recorte de pantalla de la selección de plantilla de elemento de runtime de ListView.

Para obtener más información sobre los selectores de plantillas de datos, consulta Creación de un DataTemplateSelector.

Respuesta a la selección de elementos

De manera predeterminada, la selección ListView está habilitada. Pero este comportamiento se puede cambiar modificando la propiedad SelectionMode. La enumeración ListViewSelectionMode define los miembros siguientes:

  • None: indica que no se puede seleccionar ningún elemento.
  • Single : indica que se puede seleccionar un solo elemento, con el elemento seleccionado resaltado. Este es el valor predeterminado.

ListView define un evento ItemSelected que se genera cuando cambia la propiedad SelectedItem, ya sea debido a que el usuario selecciona un elemento de la lista o cuando una aplicación establece la propiedad. El objeto SelectedItemChangedEventArgs que acompaña a este evento tiene propiedades SelectedItem y SelectedItemIndex .

Cuando la propiedad SelectionMode se establece en Single, se puede seleccionar un solo elemento en ListView. Cuando un elemento está seleccionado, la propiedad SelectedItem se establece en el valor del elemento seleccionado: Cuando la propiedad cambia, se genera el evento ItemSelected.

En el ejemplo siguiente se muestra un ListView que puede responder a la selección de un solo elemento:

<ListView ItemsSource="{Binding Monkeys}"
          ItemSelected="OnItemSelected">
    ...
</ListView>

En este ejemplo, el controlador de eventos OnItemSelected se ejecuta cuando se desencadena el evento ItemSelected, con el controlador de eventos recuperando el elemento seleccionado:

void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
    Monkey item = args.SelectedItem as Monkey;
}

En la captura de pantalla siguiente se muestra la selección de un solo elemento en una ListView:

Recorte de pantalla de un control ListView con una selección.

Borrar la selección

La propiedad SelectedItem se puede borrar estableciéndola, o el objeto al que se enlaza, en null.

Deshabilitar selección

La selección ListView está habilitada de manera predeterminada. Pero se puede deshabilitar estableciendo la propiedad SelectionMode en None:

<ListView ...
          SelectionMode="None" />

Cuando la propiedad SelectionMode se establece en None, los elementos en ListView no se pueden seleccionar, la propiedad SelectedItem permanecerá null y el evento ItemSelected no se desencadenará.

Datos de caché

ListView es una vista eficaz para mostrar datos, pero tiene algunas limitaciones. El rendimiento del desplazamiento puede sufrir al usar celdas personalizadas, especialmente cuando contienen jerarquías de vistas profundamente anidadas o usan determinados diseños que requieren una medición compleja. Afortunadamente, hay técnicas que se pueden usar para evitar un mal rendimiento de :

Un ListView a menudo se usa para mostrar mucho más datos de los que encajan en la pantalla. Por ejemplo, una aplicación de música podría tener una biblioteca de canciones con miles de entradas. La creación de un elemento para cada entrada desperdiciaría memoria valiosa y funcionaría mal. La creación y destrucción de filas constantemente requeriría que la aplicación crease instancias y limpiase objetos constantemente, lo que también funcionaría mal.

Para conservar la memoria, los equivalentes nativos ListView de cada plataforma tienen características integradas para reutilizar filas. Solo las celdas visibles en pantalla se cargan en memoria y el contenido se carga en celdas existentes. Este patrón impide que la aplicación cree instancias de miles de objetos, lo que ahorra tiempo y memoria.

.NET MAUI permite la reutilización de celdas ListView mediante la enumeración ListViewCachingStrategy, que define los siguientes miembros:

  • RetainElement, especifica que ListView generará una celda para cada elemento de la lista.
  • RecycleElement, especifica que ListView intentará minimizar su superficie de memoria y velocidad de ejecución mediante el reciclaje de las celdas de la lista.
  • RecycleElementAndDataTemplate, como RecycleElement, al mismo tiempo que también garantiza que cuando ListView usa un objeto DataTemplateSelector, los objetos DataTemplate se almacenen en caché según el tipo de elemento de la lista.

Conservar elementos

La estrategia RetainElement de almacenamiento en caché especifica que ListView generará una celda para cada elemento de la lista y es el comportamiento ListView predeterminado. Debe usarse en las siguientes circunstancias:

  • Cada celda tiene un gran número de enlaces (de 20 a 30+).
  • La plantilla de celda cambia con frecuencia.
  • Las pruebas revelan que la estrategia RecycleElement de almacenamiento en caché da como resultado una velocidad de ejecución reducida.

Es importante reconocer las consecuencias de la estrategia RetainElement de almacenamiento en caché al trabajar con celdas personalizadas. Cualquier código de inicialización de celda tendrá que ejecutarse para cada creación de celdas, que puede ser varias veces por segundo. En esta circunstancia, las técnicas de diseño que estaban bien en una página, como el uso de varios objetos Grid anidados, se convierten en cuellos de botella de rendimiento cuando se configuran y destruyen en tiempo real a medida que el usuario se desplaza.

Elementos de reciclaje

La estrategia RecycleElementde almacenamiento en caché especifica que ListView intentará minimizar la superficie de memoria y la velocidad de ejecución mediante el reciclaje de las celdas de la lista. Este modo no siempre ofrece una mejora del rendimiento y se deben realizar pruebas para determinar las mejoras. Pero es la opción preferida y debe usarse en las siguientes circunstancias:

  • Cada celda tiene un número de pequeño a moderado de enlaces.
  • Cada BindingContext de celda define todos los datos de celda.
  • Cada celda es en gran medida similar, con la plantilla de celda sin cambiar.

Durante la virtualización, la celda tendrá actualizado su contexto de enlace, por lo que si una aplicación usa este modo, debe asegurarse de que las actualizaciones de contexto de enlace se controlan correctamente. Todos los datos sobre la celda deben provenir del contexto de enlace o de los errores de coherencia. Este problema se puede evitar mediante el enlace de datos para mostrar datos de celda. Como alternativa, los datos de celda deben establecerse en la invalidación de OnBindingContextChanged, en lugar de en el constructor de la celda personalizada, como se muestra en el ejemplo siguiente:

public class CustomCell : ViewCell
{
    Image image = null;

    public CustomCell()
    {
        image = new Image();
        View = image;
    }

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();

        var item = BindingContext as ImageItem;
        if (item != null)
        {
            image.Source = item.ImageUrl;
        }
    }
}

Elementos de reciclaje con DataTemplateSelector

Cuando un ListView usa un DataTemplateSelector para seleccionar un DataTemplate, la estrategia RecycleElement de almacenamiento en caché no almacena en caché objetos DataTemplate. En su lugar, se selecciona un DataTemplate para cada elemento de datos de la lista.

Nota:

La estrategia RecycleElement de almacenamiento en caché requiere que cuando a DataTemplateSelector se le pide que seleccione un DataTemplate, cada DataTemplate debe devolver el mismo tipo de ViewCell. Por ejemplo, dado un ListView con un DataTemplateSelector que puede devolver MyDataTemplateA (donde MyDataTemplateA devuelve un ViewCell de tipo MyViewCellA), o MyDataTemplateB (donde MyDataTemplateB devuelve un ViewCell de tipo MyViewCellB), cuando MyDataTemplateA se devuelve, se debe devolver MyViewCellA o se producirá una excepción.

Elementos de reciclaje con DataTemplates

La estrategia RecycleElementAndDataTemplate de almacenamiento en caché se basa en la estrategia RecycleElement de almacenamiento en caché asegurándose además de que cuando un ListView usa un DataTemplateSelector para seleccionar un DataTemplate, los objetos DataTemplate se almacenan en caché por el tipo de elemento de la lista. Por lo tanto, los objetos DataTemplate se seleccionan una vez por tipo de elemento, en lugar de una vez por instancia de elemento.

Nota:

La estrategia RecycleElementAndDataTemplate de almacenamiento en caché requiere que los objetos DataTemplate devueltos por DataTemplateSelector deben usar el constructor DataTemplate que toma un Type.

Establecer la estrategia de almacenamiento en caché

La estrategia ListView de almacenamiento en caché se puede definir mediante en XAML estableciendo el atributo CachingStrategy:

<ListView CachingStrategy="RecycleElement">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
              ...
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

En C#, la estrategia de almacenamiento en caché se establece a través de una sobrecarga del constructor:

ListView listView = new ListView(ListViewCachingStrategy.RecycleElement);

Establecer la estrategia de almacenamiento en caché en una listView con subclase

Establecer el atributo CachingStrategy de XAML en una subclase ListView no generará el comportamiento deseado, ya que no hay ninguna propiedad CachingStrategy en ListView. La solución a este problema consiste en especificar un constructor en la subclase ListView que acepta un parámetro ListViewCachingStrategy y lo pasa a la clase base:

public class CustomListView : ListView
{
    public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
    {
    }
    ...
}

Después, el valor de enumeración ListViewCachingStrategy se puede especificar desde XAML mediante el atributo x:Arguments:

<local:CustomListView>
    <x:Arguments>
        <ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
    </x:Arguments>
</local:CustomListView>

Encabezados y pies de página

ListView puede presentar un encabezado y pie de página que se desplazan con los elementos de la lista. El encabezado y el pie de página pueden ser cadenas, vistas o objetos DataTemplate.

ListView define las siguientes propiedades para especificar el encabezado y el pie de página:

  • Header, de tipo object, especifica la cadena, el enlace o la vista que se mostrarán al principio de la lista.
  • HeaderTemplate, de tipo DataTemplate, especifica el DataTemplate que se va a usar para dar formato a Header.
  • Footer, de tipo object, especifica la cadena, el enlace o la vista que se mostrarán al final de la lista.
  • FooterTemplate, de tipo DataTemplate, especifica el DataTemplate que se va a usar para dar formato a Footer.

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

La propiedad Header y Footer se puede establecer en valores string, como se muestra en el ejemplo siguiente:

<ListView ItemsSource="{Binding Monkeys}"
          Header="Monkeys"
          Footer="2022">
    ...
</ListView>

En la captura de pantalla siguiente se muestra el encabezado resultante:

Recorte de pantalla de un encabezado de cadena ListView.

Las propiedades Header y Footer se pueden establecer en una vista. Esto puede ser una sola vista o una vista que contenga varias vistas secundarias. En el ejemplo siguiente se muestran las propiedades Header y Footer de cada conjunto en un objeto Grid que contiene un objeto Label:

<ListView ItemsSource="{Binding Monkeys}">
    <ListView.Header>
        <Grid BackgroundColor="LightGray">
            <Label Margin="10,0,0,0"
                   Text="Monkeys"
                   FontSize="12"
                   FontAttributes="Bold" />
        </Grid>
    </ListView.Header>
    <ListView.Footer>
        <Grid BackgroundColor="LightGray">
            <Label Margin="10,0,0,0"
                   Text="Friends of Monkey"
                   FontSize="12"
                   FontAttributes="Bold" />
        </Grid>
    </ListView.Footer>
    ...
</ListView>

En la captura de pantalla siguiente se muestra el encabezado resultante:

Recorte de pantalla del encabezado y el pie de página de CollectionView mediante vistas.

Las propiedades HeaderTemplate y FooterTemplate se pueden establecer en objetos DataTemplate que se usan para dar formato al encabezado y al pie de página. En este escenario, las propiedades Header y Footer deben enlazarse al origen actual para que se apliquen las plantillas, como se muestra en el ejemplo siguiente:

<ListView ItemsSource="{Binding Monkeys}"
          Header="{Binding .}"
          Footer="{Binding .}">
    <ListView.HeaderTemplate>
        <DataTemplate>
            <Grid BackgroundColor="LightGray">
                <Label Margin="10,0,0,0"
                       Text="Monkeys"
                       FontSize="12"
                       FontAttributes="Bold" />
            </Grid>
        </DataTemplate>
    </ListView.HeaderTemplate>
    <ListView.FooterTemplate>
        <DataTemplate>
            <Grid BackgroundColor="LightGray">
                <Label Margin="10,0,0,0"
                       Text="Friends of Monkey"
                       FontSize="12"
                       FontAttributes="Bold" />
            </Grid>
        </DataTemplate>
    </ListView.FooterTemplate>
    ...
</ListView>

Separadores de elementos de control

De forma predeterminada, los separadores se muestran entre elementos ListView en iOS y Android. Este comportamiento se puede cambiar modificando la propiedad SeparatorVisibility, de tipo SeparatorVisibility, en None:

<ListView ...
          SeparatorVisibility="None" />

Además, cuando el separador está habilitado, se puede establecer el color con la propiedad SeparatorColor:

<ListView ...
          SeparatorColor="Blue" />

Tamaño de los elementos

De forma predeterminada, todos los elementos de un elemento ListView tienen el mismo alto, que se deriva del contenido de que DataTemplate define la apariencia de cada elemento. Pero, este comportamiento se puede cambiar con las propiedades HasUnevenRows y RowHeight. De manera predeterminada, la propiedad HasUnevenRows es false.

La propiedad RowHeight se puede establecer en un int que representa el alto de cada elemento de ListView siempre que HasUnevenRows sea false. Cuando HasUnevenRows se establece en true, cada elemento de ListView puede tener un alto diferente. El alto de cada elemento se derivará del contenido del elemento DataTemplate, por lo que cada elemento se ajustará a su contenido.

Los elementos ListView individuales se pueden cambiar de tamaño mediante programación en tiempo de ejecución cambiando las propiedades relacionadas con el diseño de los elementos dentro de DataTemplate, siempre que la propiedad HasUnevenRows sea true. En el ejemplo siguiente se cambia el alto de un objeto Image cuando se pulsa:

void OnImageTapped(object sender, EventArgs args)
{
    Image image = sender as Image;
    ViewCell viewCell = image.Parent.Parent as ViewCell;

    if (image.HeightRequest < 250)
    {
      image.HeightRequest = image.Height + 100;
      viewCell.ForceUpdateSize();
    }
}

En este ejemplo, el controlador de eventos OnImageTapped se ejecuta en respuesta a un objeto Image que se está pulsando. El controlador de eventos actualiza el alto de Image y el método Cell.ForceUpdateSize actualiza el tamaño de la celda, incluso cuando no está visible actualmente.

Advertencia

El uso excesivo del ajuste de tamaño de elementos dinámicos puede provocar una degradación del rendimiento de ListView.

Diseño de derecha a izquierda

ListView puede diseñar su contenido en una dirección de flujo de derecha a izquierda estableciendo su propiedad RightToLeft en FlowDirection. Sin embargo, la propiedad FlowDirection debe establecerse idealmente en una página o diseño raíz, lo que hace que todos los elementos de la página o diseño raíz respondan a la dirección del flujo:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ListViewDemos.RightToLeftListPage"
             Title="Right to left list"
             FlowDirection="RightToLeft">
    <Grid Margin="20">
        <ListView ItemsSource="{Binding Monkeys}">
            ...
        </ListView>
    </Grid>
</ContentPage>

El valor predeterminado FlowDirection de un elemento con un elemento primario es MatchParent. Por lo tanto, ListView hereda el valor de la propiedad FlowDirection de Grid, que a su vez hereda el valor de la propiedad FlowDirection de ContentPage.

Mostrar datos agrupados

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.

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. Esta colección 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. En el ejemplo siguiente se muestra la clase Animal:

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. Después se puede crear un tipo que modele un grupo de elementos En el ejemplo siguiente se muestra la clase AnimalGroup:

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 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 denomina Monkeys y contiene una List<Animal> colección de detalles de monos.

ListView mostrará los datos agrupados, si los datos se han agrupado correctamente, estableciendo la propiedad IsGroupingEnabled en true:

<ListView ItemsSource="{Binding Animals}"
          IsGroupingEnabled="True">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Grid Padding="10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <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>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

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

ListView listView = new ListView
{
    IsGroupingEnabled = true
};
listView.SetBinding(ItemsView.ItemsSourceProperty, "Animals");
// ...

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

En la captura de pantalla siguiente se muestran los ListView datos agrupados:

Recorte de pantalla de los datos agrupados en un control ListView.

Nota:

De forma predeterminada, ListView mostrará el nombre del grupo en el encabezado. Este comportamiento se puede cambiar personalizando el encabezado de grupo.

Personalizar el encabezado de grupo

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

<ListView ItemsSource="{Binding Animals}"
          IsGroupingEnabled="True">
    <ListView.GroupHeaderTemplate>
        <DataTemplate>
            <ViewCell>
                <Label Text="{Binding Name}"
                       BackgroundColor="LightGray"
                       FontSize="18"
                       FontAttributes="Bold" />
            </ViewCell>
        </DataTemplate>
    </ListView.GroupHeaderTemplate>
    ...
</ListView>

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 la captura de pantalla siguiente se muestra el encabezado de grupo personalizado:

Recorte de pantalla de un encabezado de grupo personalizado en un control ListView.

Importante

La propiedad GroupHeaderTemplate se excluye mutuamente con la propiedad GroupDisplayBinding. Por lo tanto, no se deben establecer ambas propiedades.

Agrupar sin plantillas

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

<ListView ItemsSource="{Binding Animals}"
          IsGroupingEnabled="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.

Controlar el desplazamiento

ListView define dos métodos ScrollTo, que desplazan los elementos a la vista. Una de las sobrecargas desplaza el elemento especificado en la vista, mientras que el otro desplaza el elemento especificado en el grupo especificado a la vista. Las dos sobrecargas tienen argumentos adicionales que permiten especificar la posición exacta del elemento después de que se haya completado el desplazamiento y si se va a animar el desplazamiento.

ListView define un evento ScrollToRequested que se desencadena cuando se invoca uno de los métodos ScrollTo. El objeto ScrollToRequestedEventArgs que acompaña al evento ScrollToRequested tiene muchas propiedades, como ShouldAnimate, Element, Mode y Position. Algunas de estas propiedades se establecen a partir de los argumentos especificados en las llamadas al método ScrollTo.

Además, ListView define un evento Scrolled que se desencadena para indicar que se ha producido el desplazamiento. El objeto ScrolledEventArgs que acompaña al evento Scrolled tiene propiedades ScrollX y ScrollY.

Detectar desplazamiento

ListView define un evento Scrolled que se desencadena para indicar que se produjo el desplazamiento. La clase ItemsViewScrolledEventArgs, que representa al objeto que acompaña al evento Scrolled, define las siguientes propiedades:

  • ScrollX, de tipo double, representa la posición X del desplazamiento.
  • ScrollY, de tipo double, representa la posición Y del desplazamiento.

En el ejemplo de XAML siguiente se muestra ListView, que establece un controlador para el evento Scrolled:

<ListView Scrolled="OnListViewScrolled">
    ...
</ListView>

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

ListView listView = new ListView();
listView.Scrolled += OnListViewScrolled;

En este ejemplo de código, el controlador de eventos OnListViewScrolled se ejecuta cuando se desencadena el evento Scrolled:

void OnListViewScrolled(object sender, ScrolledEventArgs e)
{
    // Custom logic
}

Importante

El evento Scrolled se desencadena para los desplazamientos iniciados por el usuario y para los desplazamientos mediante programación.

Desplazar un elemento a la vista

El método ScrollTo desplaza el elemento especificado en la vista. Dado un objeto ListView denominado listView, en el ejemplo siguiente se muestra cómo desplazar el elemento mono násico a la vista:

MonkeysViewModel viewModel = BindingContext as MonkeysViewModel;
Monkey monkey = viewModel.Monkeys.FirstOrDefault(m => m.Name == "Proboscis Monkey");
listView.ScrollTo(monkey, ScrollToPosition.MakeVisible, true);

Como alternativa, un elemento de los datos agrupados se puede desplazar a la vista especificando el elemento y el grupo. En el ejemplo siguiente se muestra cómo desplazar el elemento mono násico en el grupo Monos en la vista:

GroupedAnimalsViewModel viewModel = BindingContext as GroupedAnimalsViewModel;
AnimalGroup group = viewModel.Animals.FirstOrDefault(a => a.Name == "Monkeys");
Animal monkey = group.FirstOrDefault(m => m.Name == "Proboscis Monkey");
listView.ScrollTo(monkey, group, ScrollToPosition.MakeVisible, true);

Nota:

El evento ScrollToRequested se desencadena cuando se invoca el método ScrollTo.

Deshabilitar la animación de desplazamiento

Se muestra una animación de desplazamiento al desplazar un elemento a la vista. Pero esta animación se puede deshabilitar estableciendo el argumento animated del método ScrollTo en false:

listView.ScrollTo(monkey, position: ScrollToPosition.MakeVisible, animate: false);

Controlar la posición del desplazamiento

Al desplazarse por un elemento a la vista, se puede especificar la posición exacta del elemento después de que se haya completado el desplazamiento con el argumento position de los métodos ScrollTo. Este argumento acepta un miembro de enumeración ScrollToPosition.

MakeVisible

El miembro ScrollToPosition.MakeVisible indica que el elemento debe desplazarse hasta que esté visible en la vista:

listView.ScrollTo(monkey, position: ScrollToPosition.MakeVisible, animate: true);

Start

El miembro ScrollToPosition.Start indica que el elemento debe desplazarse hasta el inicio de la vista:

listView.ScrollTo(monkey, position: ScrollToPosition.Start, animate: true);

Centrar

El miembro ScrollToPosition.Center indica que el elemento debe desplazarse al centro de la vista:

listView.ScrollTo(monkey, position: ScrollToPosition.Center, animate: true);

Final

El miembro ScrollToPosition.End indica que el elemento debe desplazarse al final de la vista:

listView.ScrollTo(monkey, position: ScrollToPosition.End, animate: true);

Visibilidad de la barra de desplazamiento

ListView define las propiedades HorizontalScrollBarVisibility y VerticalScrollBarVisibility, respaldadas por propiedades enlazables. Estas propiedades obtienen o establecen un valor de enumeración ScrollBarVisibility que representa cuando la barra de desplazamiento horizontal o vertical está visible. La enumeración ScrollBarVisibility define los miembros siguientes:

  • Default indica el comportamiento predeterminado de la barra de desplazamiento para la plataforma y es el valor predeterminado de las propiedades HorizontalScrollBarVisibility y VerticalScrollBarVisibility .
  • Always indica que las barras de desplazamiento estarán visibles, incluso cuando el contenido se ajusta en la vista.
  • Never indica que las barras de desplazamiento no están visibles, incluso si el contenido no encaja en la vista.

Agregar menús contextuales

ListView admite elementos de menús contextuales, que se definen como objetos MenuItem que se agregan a la colección ViewCell.ContextActions en la DataTemplate de cada elemento:

<ListView x:Name="listView"
          ItemsSource="{Binding Monkeys}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <ViewCell.ContextActions>
                    <MenuItem Text="Favorite"
                              Command="{Binding Source={x:Reference listView}, Path=BindingContext.FavoriteCommand}"
                              CommandParameter="{Binding}" />
                    <MenuItem Text="Delete"
                              Command="{Binding Source={x:Reference listView}, Path=BindingContext.DeleteCommand}"
                              CommandParameter="{Binding}" />
                </ViewCell.ContextActions>

                ...
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Los objetos MenuItem se muestran cuando se hace clic con el botón derecho en un elemento de la ListView :

Recorte de pantalla de los elementos de menú contextual de CollectionView.

Para obtener más información sobre los elementos de menú, consulta Mostrar elementos de menú.

Deslizar para actualizar

ListView admite la extracción para actualizar la funcionalidad, lo que permite actualizar los datos que se muestran mediante la extracción en la lista de elementos.

Para habilitar la extracción para actualizar, establezca la propiedad IsPullToRefreshEnabled en true. Cuando se desencadena una actualización, ListView genera el evento Refreshing y la propiedad IsRefreshing se establecerá en true. Después, el controlador del evento Refreshing debe ejecutar el código necesario para actualizar el contenido del ListView o la implementación de ICommand que ejecuta RefreshCommand tiene que ejecutarlo. Una vez actualizada ListView, la propiedad IsRefreshing debe establecerse en false o se debe llamar al método EndRefresh en la ListView para indicar que la actualización se ha completado.

En el ejemplo siguiente se muestra una ListView que usa la extracción para actualizar:

<ListView ItemsSource="{Binding Animals}"
          IsPullToRefreshEnabled="true"
          RefreshCommand="{Binding RefreshCommand}"
          IsRefreshing="{Binding IsRefreshing}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                ...
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

En este ejemplo, cuando el usuario inicia una actualización, se ejecuta el ICommand definido por la propiedad RefreshCommand, que debe actualizar los elementos que se muestran. Se muestra una visualización de actualización mientras se produce, que consta de un círculo de progreso animado. El valor de la propiedad IsRefreshing indica el estado actual de la operación de restauración de la tabla. Cuando se desencadena una actualización, esta propiedad pasará automáticamente a true. Una vez completada la actualización, debe restablecer la propiedad a false.