ListView
.NET Multi-platform App UI (.NET MAUI) ListView 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 puedes definir celdas personalizadas que muestren cualquier contenido que desees. ListView también incluye compatibilidad para mostrar encabezados y pies de página, datos agrupados, deslizar para actualizar y elementos de menú contextual.
El clase ListView se deriva de la clase ItemsView<Cell>
, de la que hereda las propiedades siguientes:
ItemsSource
, de tipoIEnumerable
, especifica la colección de elementos que se mostrarán, y tiene un valor predeterminado denull
.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 tipoobject
, 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 aFooter
.GroupHeaderTemplate
, de tipo DataTemplate, define el DataTemplate utilizado para definir la apariencia del encabezado de cada grupo. Esta propiedad es mutuamente excluyente con la propiedadGroupDisplayBinding
. Por lo tanto, configurar esta propiedad estableceráGroupDisplayBinding
ennull
.HasUnevenRows
, de tipobool
, indica si los elementos de la lista pueden tener filas de diferentes altos. El valor predeterminado de esta propiedad esfalse
.Header
, de tipoobject
, 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 aHeader
.HorizontalScrollBarVisibility
, de tipoScrollBarVisibility
, indica cuándo estará visible la barra de desplazamiento horizontal.IsGroupedEnabled
, de tipobool
, indica si los datos subyacentes deben mostrarse en grupos. El valor predeterminado de esta propiedad esfalse
.IsPullToRefreshEnabled
, de tipobool
, indica si el usuario puede deslizar el dedo hacia abajo para hacer que ListView actualice sus datos. El valor predeterminado de esta propiedad esfalse
.IsRefreshing
, de tipobool
, indica si el ListView se está actualizando actualmente. El valor predeterminado de esta propiedad esfalse
.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 tipoint
, determina el alto de cada fila cuandoHasUnevenRows
esfalse
.SelectedItem
, de tipoobject
, representa el elemento seleccionado actualmente en ListView.SelectionMode
, de tipoListViewSelectionMode
, indica si los elementos se pueden seleccionar en ListView o no. El valor predeterminado de esta propiedad esSingle
.SeparatorColor
, de tipo Color, define el color de la barra que separa los elementos de la lista.SeparatorVisibility
, de tipoSeparatorVisibility
, define si los separadores son visibles entre los elementos.VerticalScrollBarVisibility
, de tipoScrollBarVisibility
, indica cuándo estará visible la barra de desplazamiento vertical.
Todas estas propiedades están respaldadas por objetos BindableProperty, lo que significa que pueden ser destinos de enlaces de datos y estilo.
Además, ListView define las siguientes propiedades que no están respaldadas por objetos BindableProperty:
GroupDisplayBinding
, de tipoBindingBase
, el enlace que se usará para mostrar el encabezado de grupo. Esta propiedad es mutuamente excluyente con la propiedadGroupHeaderTemplate
. Por lo tanto, configurar esta propiedad estableceráGroupHeaderTemplate
ennull
.GroupShortNameBinding
, de tipoBindingBase
, el enlace del nombre que se va a mostrar en listas de accesos directos agrupados.CachingStrategy
, de tipoListViewCachingStrategy
, 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 objetoItemVisibilityEventArgs
que acompaña a este evento define las propiedadesItem
yIndex
:ItemDisappearing
, que se produce cuando la representación visual de un elemento se quita del diseño visual de ListView. El objetoItemVisibilityEventArgs
que acompaña a este evento define las propiedadesItem
yIndex
.ItemSelected
, que se genera cuando se selecciona un nuevo elemento de la lista. El objetoSelectedItemChangedEventArgs
que acompaña a este evento define las propiedadesSelectedItem
ySelectedItemIndex
.ItemTapped
, que se genera cuando se pulsa un elemento en ListView. El objetoItemTappedEventArgs
que acompaña a este evento define las propiedadesGroup
,Item
yItemIndex
.Refreshing
, que se genera cuando se desencadena una operación de deslizar para actualizar en ListView.Scrolled
, . El objetoScrolledEventArgs
que acompaña a este evento define las propiedadesScrollX
yScrollY
.ScrollToRequested
. El objetoScrollToRequestedEventArgs
que acompaña a este evento define las propiedades Element,Mode
,Position
,ScrollX
,ScrollY
yShouldAnimate
.
Relleno de ListView con datos
ListView se rellena con datos estableciendo su propiedad ItemsSource
en cualquier colección que implemente IEnumerable
.
Importante
Si se necesita ListView para actualizar a medida que se agregan, quitan o cambian elementos en la colección subyacente, la colección subyacente debe ser una colección IEnumerable
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 con 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 datos de la propiedad ItemsSource
se enlazan a la propiedad Monkeys
del modelo de vista conectado.
Nota:
Los enlaces compilados se pueden habilitar para mejorar el rendimiento del enlace de datos en aplicaciones .NET MAUI. 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 en ListView mediante el establecimiento de la propiedad ItemTemplate
en 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 DataTemplate definen la apariencia de cada elemento de la lista y el elemento secundario de DataTemplate debe ser un objeto Cell. En este ejemplo, el diseño dentro de DataTemplate se administra mediante Grid. 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; }
}
En la captura de pantalla siguiente se muestra el resultado de crear plantillas para cada elemento de la lista:
Para obtener más información sobre las plantillas de datos, consulta Plantillas de datos.
Celdas
La apariencia de cada elemento de 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 primario y secundario en líneas diferentes.
- ImageCell, que muestra una imagen con texto primario y secundario en líneas diferentes.
- SwitchCell, que muestra texto y un conmutador que se puede 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 View. Este tipo de celda debe usarse cuando desees definir completamente la apariencia de cada elemento de un ListView.
Normalmente, SwitchCell y EntryCell solo se usarán en TableView y no se usarán en ListView. Para obtener más información sobre SwitchCell y EntryCell, consulta TableView.
Celda de texto
TextCell muestra un texto primario y secundario en líneas diferentes. TextCell define las siguientes propiedades:
Text
, de tipostring
, define el texto primario que se va a mostrar.TextColor
, de tipo Color, representa el color del texto primario.Detail
, de tipostring
, 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 tipoobject
, 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 con estilo.
En el ejemplo siguiente se muestra cómo usar TextCell para definir la apariencia de elementos en 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:
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. 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.
En el ejemplo siguiente se muestra cómo usar la apariencia de ImageCell para definir el aspecto de los elementos en 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:
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. 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.
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 ViewCell para definir la apariencia de elementos en 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 mediante Grid. Grid contiene un objeto Image y dos objetos Label, que se enlazan todos a las propiedades de la clase Monkey
.
En la captura de pantalla siguiente se muestra el resultado de crear plantillas para cada elemento de la lista:
Elección de la apariencia del elemento en tiempo de ejecución
La apariencia de cada elemento de ListView se puede elegir en tiempo de ejecución, 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>
<StackLayout Margin="20">
<ListView ItemsSource="{Binding Monkeys}"
ItemTemplate="{StaticResource MonkeySelector}" />
</StackLayout>
</ContentPage>
La propiedad ItemTemplate
se establece en un 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 las propiedades AmericanMonkey
y OtherMonkey
DataTemplate que se establecen en plantillas de datos diferentes. La invalidación de OnSelectTemplate
devuelve la plantilla AmericanMonkey
, que muestra el nombre de mono y la ubicación en verde azulado, cuando el nombre de mono contiene "America". Cuando el nombre de mono no contiene "America", la invalidación de OnSelectTemplate
devuelve la plantilla OtherMonkey
, que muestra el nombre de mono y la ubicación en plateado:
Para más información sobre los selectores de plantillas de datos, consulta Creación de 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, resaltando el elemento seleccionado. 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 las 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 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
y el controlador de eventos recupera 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 ListView:
Borrar la selección
La propiedad SelectedItem
se puede borrar estableciéndola o estableciendo el objeto al que se enlaza, en null
.
Deshabilitación de la 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
está establecida en None
, los elementos de ListView no se pueden seleccionar, la propiedad SelectedItem
permanecerá como 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 puedes usar para evitar un rendimiento deficiente.
A menudo ListView se usa para mostrar muchos más datos de los que caben 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 el funcionamiento no sería bueno. La creación y destrucción de filas constantemente requeriría que la aplicación cree instancias y limpie objetos constantemente, lo que también funcionaría mal.
Para conservar la memoria, los equivalentes ListView nativos de cada plataforma tienen características integradas para reutilizar filas. Solo las celdas visibles en pantalla se cargan en la 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
, comoRecycleElement
, al mismo tiempo que garantiza que cuando ListView usa un DataTemplateSelector, los objetos DataTemplate se almacenan en caché por el tipo de elemento de la lista.
Conservar elementos
La estrategia de almacenamiento en caché RetainElement
especifica que ListView generará una celda para cada elemento de la lista y es el comportamiento predeterminado de ListView. 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 de almacenamiento en caché
RecycleElement
da como resultado una velocidad de ejecución reducida.
Es importante reconocer las consecuencias de la estrategia de almacenamiento en caché RetainElement
al trabajar con celdas personalizadas. Cualquier código de inicialización de celda tendrá que ejecutarse para cada creación de celda, 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 StackLayout 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 de almacenamiento en caché RecycleElement
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 cualquier mejora. Pero es la opción preferida y debe usarse en las siguientes circunstancias:
- Cada celda tiene un número pequeño a moderado de enlaces.
- Cada celda
BindingContext
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á su contexto de enlace actualizado, 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 se pueden producir errores de coherencia. Este problema se puede evitar usando el enlace de datos para mostrar datos de celda. Como alternativa, los datos de celda deben establecerse en la invalidación 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 DataTemplateSelector para seleccionar un DataTemplate, la estrategia de almacenamiento en caché RecycleElement
no almacena en caché objetos DataTemplate. En su lugar, se selecciona un DataTemplate para cada elemento de datos de la lista.
Nota:
La estrategia de almacenamiento en caché RecycleElement
requiere que cuando se pida a DataTemplateSelector que seleccione un DataTemplate que cada DataTemplate debe devolver el mismo tipo 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 de almacenamiento en caché RecycleElementAndDataTemplate
se basa en la estrategia de almacenamiento en caché RecycleElement
, 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 de almacenamiento en caché RecycleElementAndDataTemplate
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 de almacenamiento en caché ListView 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 de constructor:
ListView listView = new ListView(ListViewCachingStrategy.RecycleElement);
Establecer la estrategia de almacenamiento en caché en una ListView subclasada
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 clase con subclases 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 u objetos DataTemplate.
ListView define las siguientes propiedades para especificar el encabezado y el pie de página:
Header
, de tipoobject
, 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 aHeader
.Footer
, de tipoobject
, 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 aFooter
.
Todas estas propiedades están respaldadas por objetos BindableProperty, lo que significa que las propiedades pueden ser destinos de los enlaces de datos.
Mostrar cadenas en el encabezado y el pie de página
La propiedad Header
y Footer
se puede establecer en valores string
, como se muestra en el siguiente ejemplo:
<ListView ItemsSource="{Binding Monkeys}"
Header="Monkeys"
Footer="2022">
...
</ListView>
En la captura de pantalla siguiente se muestra el encabezado resultante:
Mostrar vistas en el encabezado y el pie de página
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 siguiente ejemplo, se muestran las propiedades Header
y Footer
, cada una establecida en un objeto StackLayout que contiene un objeto Label:
<ListView ItemsSource="{Binding Monkeys}">
<ListView.Header>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Monkeys"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</ListView.Header>
<ListView.Footer>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Friends of Monkey"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</ListView.Footer>
...
</ListView>
En la captura de pantalla siguiente se muestra el encabezado resultante:
Mostrar un encabezado y pie de página con plantilla
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 de la plantilla que se va a aplicar, como se muestra en el siguiente ejemplo:
<ListView ItemsSource="{Binding Monkeys}"
Header="{Binding .}"
Footer="{Binding .}">
<ListView.HeaderTemplate>
<DataTemplate>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Monkeys"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.FooterTemplate>
<DataTemplate>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Friends of Monkey"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</DataTemplate>
</ListView.FooterTemplate>
...
</ListView>
Separadores de elementos de control
De manera predeterminada, los separadores se muestran entre los elementos ListView en iOS y Android. Este comportamiento se puede modificar estableciendo 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 manera predeterminada, todos los elementos de un ListView tienen el mismo alto, que se deriva del contenido del DataTemplate que 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 DataTemplate del elemento, por lo que cada elemento se ajustará a su contenido.
El tamaño de cada uno de los elementos ListView se puede cambiar mediante programación en runtime cambiando las propiedades del elemento relacionadas con el diseño 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 dinámico de los elementos 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 FlowDirection
en RightToLeft
. 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 del 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">
<StackLayout Margin="20">
<ListView ItemsSource="{Binding Monkeys}">
...
</ListView>
</StackLayout>
</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 StackLayout, que a su vez hereda el valor de la propiedad FlowDirection
de ContentPage.
Visualización de datos agrupados
Los conjuntos de datos de gran tamaño se pueden volver difíciles de manejar cuando se presentan en una lista de desplazamiento continuo. En este escenario, la agrupación de los datos puede mejorar la experiencia del usuario al facilitar la navegación.
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:
- Un 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>
, dondeT
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 datos 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. Luego 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 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 llamada 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
.
Después los datos agrupados se pueden agregar a la colección Animals
:
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 grupo AnimalGroup
se llama Bears
y contiene una colección List<Animal>
de detalles del portador. El segundo AnimalGroup
se llama Monkeys
y contiene una colección List<Animal>
de detalles de mono.
ListView mostrará los datos agrupados, siempre que los datos se hayan 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 ListView se define estableciendo su propiedad ItemTemplate
en DataTemplate. Para obtener más información, consulta Definir la apariencia del elemento.
En la captura de pantalla siguiente muestra ListView mostrando datos agrupados:
Nota:
De forma predeterminada, ListView mostrará el nombre del grupo en el encabezado del grupo. Este comportamiento se puede cambiar personalizando el encabezado de grupo.
Personalización del encabezado de grupo
Se puede personalizar la apariencia de cada encabezado de grupo estableciendo la propiedad ListView.GroupHeaderTemplate
en un objeto 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 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:
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 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.
Control del desplazamiento
ListView define dos métodos ScrollTo
, que desplazan los elementos en 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 en la vista. Ambas 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, incluidas ShouldAnimate
, Element, Mode
y Position
. Algunas de estas propiedades se establecen a partir de los argumentos especificados en las llamadas del 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 desplazamiento. La clase ItemsViewScrolledEventArgs
, que representa al objeto que acompaña al evento Scrolled
, define las siguientes propiedades:
ScrollX
, de tipodouble
, representa la posición X del desplazamiento.ScrollY
, de tipodouble
, 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 ScrollTo
método desplaza el elemento especificado a la vista. Dado un objeto ListView denominado listView
, en el ejemplo siguiente se muestra cómo desplazar el elemento Proboscis Monkey 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 Proboscis Monkey en el grupo Monkeys a 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
Una animación de desplazamiento se muestra 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);
Posición de desplazamiento del control
Al desplazar 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);
Fin
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
, que están 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 propiedadesHorizontalScrollBarVisibility
yVerticalScrollBarVisibility
.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 DataTemplate para 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 ListView:
Para obtener más información sobre los elementos de menú, consulta Mostrar elementos de menú.
Extraer para actualizar
ListView admite la funcionalidad deslizar para actualizar, lo que permite actualizar los datos que se muestran deslizando hacia abajo en la lista de elementos.
Para habilitar deslizar para actualizar, establece la propiedad IsPullToRefreshEnabled
en true
. Cuando se desencadena una actualización, ListView genera el evento Refreshing
y la propiedad IsRefreshing
se establecerá en true
. El código necesario para actualizar el contenido del ListView debe ser ejecutado entonces por el controlador para el evento Refreshing
o mediante la implementación de ICommand que ejecuta RefreshCommand
. Una vez actualizado ListView, la propiedad IsRefreshing
debe establecerse en false
o se debe llamar al método EndRefresh
en ListView, para indicar que la actualización se ha completado.
En el ejemplo siguiente se muestra un ListView que usa deslizar 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. Mientras se produce la actualización se muestra una visualización de actualización, que consiste en un círculo de progreso animado. La propiedad IsRefreshing
indica el estado actual de la operación de actualización. Cuando se desencadena una actualización, esta propiedad pasará automáticamente a true
. Una vez completada la actualización, debes restablecer la propiedad a false
.
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de