Resumen del capítulo 19 Vistas de colecciones
Nota:
Este libro se publicó en la primavera de 2016 y no se ha actualizado desde entonces. Gran parte del libro sigue siendo útil, pero algunos de los materiales están anticuados y algunos temas ya no son completamente correctos o completos.
Xamarin.Forms define tres vistas que mantienen colecciones y muestran sus elementos:
Picker
es una lista relativamente breve de elementos de cadena que permite al usuario elegir uno.ListView
suele ser una lista larga de elementos que normalmente tienen el mismo tipo y formato, lo que también permite al usuario elegir uno.TableView
es una colección de celdas (normalmente de varios tipos y aspectos visuales) para mostrar datos o administrar la entrada del usuario.
Es común que las aplicaciones MVVM usen ListView
para mostrar una colección seleccionable de objetos.
Opciones de programa con el selector
El selector Picker
es una buena opción si necesita permitir que el usuario elija una opción de entre una lista relativamente breve de elementos de string
.
Selector y control de eventos
En el ejemplo PickerDemo se muestra cómo usar XAML para establecer la propiedad Picker
Title
y agregar elementos de string
a la colección Items
. Cuando el usuario selecciona Picker
, se muestran los elementos de la colección Items
de una manera dependiente de la plataforma.
El evento SelectedIndexChanged
indica cuando el usuario ha seleccionado un elemento. La propiedad SelectedIndex
basada en cero indica el elemento seleccionado. Si no se selecciona ningún elemento, SelectedIndex
es igual a –1.
También puede usar SelectedIndex
para inicializar el elemento seleccionado, pero debe establecerse después de que se rellene la colección de Items
. En XAML, esto significa que probablemente utilizará un elemento de propiedad para establecer SelectedIndex
.
Enlace de datos con Picker
La propiedad SelectedIndex
está respaldada por una propiedad enlazable, pero Items
no lo está, por lo que es difícil usar el enlace de datos con Picker
. Una solución es usar Picker
junto con un objeto ObjectToIndexConverter
como el de la biblioteca Xamarin.FormsBook.Toolkit. PickerBinding muestra cómo funciona esto.
Nota:
El objeto Xamarin.FormsPicker
ahora incluye ItemsSource
las propiedades y SelectedItem
que admiten el enlace de datos. Consulte Picker.
Representación de datos con ListView
El objeto ListView
es la única clase que se deriva de ItemsView<TVisual>
de la que hereda las propiedades ItemsSource
y ItemTemplate
.
ItemsSource
es de tipo IEnumerable
pero es null
de forma predeterminada y se debe inicializar explícitamente o (más comúnmente) establecer en una colección a través de un enlace de datos. Los elementos de esta colección pueden ser de cualquier tipo.
ListView
define una propiedad SelectedItem
que se establece en uno de los elementos de la colección de ItemsSource
o null
si no se selecciona ningún elemento. ListView
activa el evento ItemSelected
cuando se selecciona un nuevo elemento.
Colecciones y selecciones
El ejemplo ListViewList rellena una vista ListView
con 17 valores Color
en una colección de List<Color>
. Los elementos se pueden seleccionar, pero de forma predeterminada se muestran con sus representaciones ToString
no atractivas. Varios ejemplos de este capítulo muestran cómo corregir esa presentación y hacerla tan atractiva como se quiera.
Separador de filas
En las pantallas de iOS y Android, una línea fina separa las filas. Puede controlar esto con las propiedades SeparatorVisibility
y SeparatorColor
. La propiedad SeparatorVisibility
es de tipo SeparatorVisibility
, una enumeración con dos miembros:
Enlace de datos con el elemento seleccionado
La propiedad SelectedItem
se complementa con una propiedad enlazable, por tanto puede ser el origen o destino de un enlace de datos. Su valor de BindingMode
predeterminado es OneWayToSource
, pero generalmente es el destino de un enlace de datos bidireccional, especialmente en escenarios de MVVM. En el ejemplo ListViewArray se muestra este tipo de enlace.
Diferencia de ObservableCollection
El ejemplo ListViewLogger establece la propiedad ItemsSource
de un ListView
en una colección List<DateTime>
y, a continuación, agrega progresivamente un nuevo objeto DateTime
a la colección cada segundo mediante un temporizador.
Sin embargo, la vista ListView
no se actualiza automáticamente porque la colección de List<T>
no tiene un mecanismo de notificación para indicar cuándo se agregan o quitan elementos de la colección.
Una clase mucho mejor para usarse en estos escenarios es ObservableCollection<T>
que se define en el espacio de nombres System.Collections.ObjectModel
. Esta clase implementa la interfaz INotifyCollectionChanged
y, por consiguiente, desencadena un evento CollectionChanged
cuando se agregan o quitan elementos de la colección, o cuando se reemplazan o se mueven dentro de la colección. Cuando la vista ListView
detecta internamente que una clase que implementa INotifyCollectionChanged
se ha establecido en su propiedad ItemsSource
, adjunta un controlador al evento CollectionChanged
y actualiza su presentación cuando cambia la colección.
El ejemplo ObservableLogger muestra el uso de ObservableCollection
.
Plantillas y celdas
De forma predeterminada, una vista ListView
muestra los elementos de su colección mediante el método ToString
de cada elemento. Un enfoque mejor implica definir una plantilla para mostrar los elementos.
Para experimentar con esta característica, puede usar la clase NamedColor
en la biblioteca Xamarin.FormsBook.Toolkit. Esta clase define una propiedad All
estática de tipo IList<NamedColor>
que contiene 141 objetos NamedColor
correspondientes a los campos públicos de la estructura Color
.
En el ejemplo NaiveNamedColorList se establece el objeto ItemsSource
de una vista ListView
en esta propiedad NamedColor.All
, pero solo se muestran los nombres de clase completos de los objetos NamedColor
.
ListView
necesita una plantilla para mostrar estos elementos. En el código, puede establecer la propiedad ItemTemplate
definida por ItemsView<TVisual>
en un objeto DataTemplate
mediante el constructor DataTemplate
que hace referencia a un derivado de la clase Cell
. Cell
tiene cinco derivados:
TextCell
— contiene dosLabel
vistas (conceptualmente hablando)ImageCell
— agrega unaImage
vista deTextCell
EntryCell
— contiene unaEntry
vista con unLabel
SwitchCell
— contiene unSwitch
con unLabel
ViewCell
— puede ser cualquierView
(probablemente con elemento secundarios)
A continuación, llame a SetValue
y SetBinding
en el objeto DataTemplate
para asociar los valores a las propiedades Cell
o para establecer enlaces de datos en las propiedades Cell
que hacen referencia a las propiedades de los elementos de la colección ItemsSource
. Esto se muestra en el ejemplo TextCellListCode.
A medida que la vista ListView
muestra cada elemento, se crea un pequeño árbol visual a partir de la plantilla y se establecen enlaces de datos entre el elemento y las propiedades de los elementos de este árbol visual. Puede hacerse una idea de este proceso mediante la instalación de los controladores de los eventos ItemAppearing
y ItemDisappearing
de la vista ListView
, o bien mediante un constructor DataTemplate
que utiliza una función a la que se llama cada vez que se debe crear el árbol visual de un elemento.
En el ejemplo TextCellListXaml se muestra un programa idéntico funcionalmente en XAML. Se establece una etiqueta DataTemplate
en la propiedad ItemTemplate
de la vista ListView
y, a continuación, el objeto TextCell
se establece en DataTemplate
. Los enlaces a las propiedades de los elementos de la colección se establecen directamente en las propiedades Text
y Detail
de TextCell
.
Celdas personalizadas
En XAML es posible establecer un objeto ViewCell
en DataTemplate
y, a continuación, definir un árbol visual personalizado como la propiedad View
de ViewCell
. (View
es la propiedad de contenido de ViewCell
, por lo que no se requieren las etiquetas ViewCell.View
). En el ejemplo CustomNamedColorList se muestra esta técnica:
Obtener el tamaño correcto para todas las plataformas puede resultar complicado. La propiedad RowHeight
es útil, pero en algunos casos querrá recurrir a la propiedad HasUnevenRows
, que es menos eficaz, pero obliga a la vista ListView
a ajustar el tamaño de las filas. Para iOS y Android, debe usar una de estas dos propiedades para obtener un tamaño de fila adecuado.
Agrupación de los elementos de ListView
ListView
admite la agrupación de los elementos y la navegación entre esos grupos. La ItemsSource
propiedad debe establecerse en una colección de colecciones: el objeto establecido ItemsSource
debe implementar IEnumerable
, y cada elemento de la colección también debe implementar IEnumerable
. Cada grupo debe incluir dos propiedades: una descripción de texto del grupo y una abreviatura de tres letras.
La clase NamedColorGroup
de la biblioteca Xamarin.FormsBook.Toolkit crea siete grupos de objetos NamedColor
. En el ejemplo ColorGroupList se muestra cómo usar estos grupos con la propiedad IsGroupingEnabled
de ListView
establecida en true
y las propiedades GroupDisplayBinding
y GroupShortNameBinding
enlazadas a las propiedades de cada grupo.
Encabezados de grupo personalizados
Para crear encabezados personalizados para los grupos de ListView
, reemplace la propiedad GroupDisplayBinding
por GroupHeaderTemplate
que define una plantilla para los encabezados.
ListView e interactividad
Por lo general, una aplicación obtiene la interacción del usuario con una vista ListView
adjuntando un controlador al evento ItemSelected
o ItemTapped
, o estableciendo un enlace de datos en la propiedad SelectedItem
. Pero algunos tipos de celda (EntryCell
y SwitchCell
) permiten la interacción con el usuario, y también es posible crear celdas personalizadas que interactúen con el usuario. InteractiveListView crea 100 instancias de ColorViewModel
y permite que el usuario cambie cada color mediante un trío de elementos Slider
. El programa también hace uso de ColorToContrastColorConverter
en la biblioteca Xamarin.FormsBook.Toolkit.
ListView y MVVM
ListView
desempeña un importante rol en los escenarios de MVVM. Cuando una colección de IEnumerable
existe en un objeto ViewModel, a menudo se enlaza a una vista ListView
. Además, los elementos de la colección suelen implementar INotifyPropertyChanged
para enlazar con las propiedades de una plantilla.
Colección de ViewModels
Para explorar esto último, la biblioteca SchoolOfFineArts crea varias clases basadas en un archivo de datos XML e imágenes de alumnos ficticios en esta escuela ficticia.
La clase Student
deriva de ViewModelBase
. La clase StudentBody
es una colección de objetos Student
y también deriva de ViewModelBase
. En SchoolViewModel
se descarga el archivo XML y se ensamblan todos los objetos.
El programa StudentList usa un objeto ImageCell
para mostrar los alumnos y sus imágenes en una vista ListView
:
El ejemplo ListViewHeader agrega una propiedad Header
, pero solo se muestra en Android.
Selección y contexto de enlace
El programa SelectedStudentDetail enlaza el objeto BindingContext
de una vista StackLayout
con la propiedad SelectedItem
de la vista ListView
. Esto permite que el programa muestre información detallada acerca del alumno seleccionado.
Menús contextuales
Una celda puede definir un menú contextual que se implementa de forma específica de la plataforma. Para crear este menú, agregue objetos MenuItem
a la propiedad ContextActions
de la Cell
.
La clase MenuItem
define cinco propiedades:
Text
de tipostring
Icon
de tipoFileImageSource
IsDestructive
de tipobool
Command
de tipoICommand
CommandParameter
de tipoobject
Las propiedades Command
y CommandParameter
implican que el objeto ViewModel de cada elemento contiene métodos para llevar a cabo los comandos de menú deseados. En escenarios no MVVM, MenuItem
también define un evento Clicked
.
El elemento CellContextMenu muestra esta técnica. La propiedad Command
de cada elemento MenuItem
se enlaza a una propiedad de tipo ICommand
en la clase Student
. Establezca la propiedad IsDestructive
en true
para un elemento MenuItem
que quite o elimine el objeto seleccionado.
Variación de los objetos visuales
A veces, querrá pequeñas variaciones de los objetos visuales de los elementos de la vista ListView
en función de una propiedad. Por ejemplo, cuando el promedio de puntuación de un alumno cae por debajo de 2,0, la propiedad ColorCodedStudents muestra el nombre del alumno en rojo.
Esto se logra mediante el uso de un convertidor de valores de enlace, ThresholdToObjectConverter
, en la biblioteca Xamarin.FormsBook.Toolkit.
Actualización de la memoria caché
La vista ListView
admite un movimiento de desactivación para actualizar sus datos. El programa debe establecer la propiedad IsPullToRefresh
en true
para habilitarlo. La vista ListView
responde al movimiento de la desactivación mediante el establecimiento de su propiedad IsRefreshing
en true
, mediante la activación del evento Refreshing
(para escenarios de MVVM) y con la llamada al método Execute
de su propiedad RefreshCommand
.
El código que controla el evento Refresh
o RefreshCommand
posiblemente actualiza los datos mostrados por la vista ListView
y vuelve a establecer la propiedad IsRefreshing
en false
.
En el ejemplo RssFeed se muestra el uso de un objeto RssFeedViewModel
que implementa las propiedades RefreshCommand
y IsRefreshing
para el enlace de datos.
TableView y sus intenciones
Aunque la vista ListView
generalmente muestra varias instancias del mismo tipo, la vista TableView
generalmente se centra en proporcionar una interfaz de usuario para varias propiedades de varios tipos. Cada elemento está asociado a su propio objeto Cell
derivado para mostrar la propiedad o para proporcionarle una interfaz de usuario.
Propiedades y jerarquías
TableView
define solo cuatro propiedades:
Intent
de tipoTableIntent
, una enumeraciónRoot
de tipoTableRoot
, la propiedad de contenido deTableView
RowHeight
de tipoint
HasUnevenRows
de tipobool
La enumeración TableIntent
indica cómo desea usar TableView
:
Estos miembros también sugieren algunos usos para TableView
.
Hay varias clases relacionadas con la definición de una tabla:
TableSectionBase
es una clase abstracta que deriva deBindableObject
y define una propiedadTitle
.TableSectionBase<T>
es una clase abstracta que deriva deTableSectionBase
e implementaIList<T>
yINotifyCollectionChanged
.TableSection
deriva deTableSectionBase<Cell>
.TableRoot
deriva deTableSectionBase<TableSection>
.
En resumen, la vista TableView
tiene una propiedad Root
que se establece en un objeto TableRoot
, que es una colección de objetos TableSection
, cada uno de los cuales es una colección de objetos Cell
. Una tabla tiene varias secciones y cada sección tiene varias celdas. La propia tabla puede tener un título y cada sección puede tener un título. Aunque la vista TableView
usa derivados del objeto Cell
, no hace uso de DataTemplate
.
Formulario prosaico
En el ejemplo EntryForm se define un modelo de vista PersonalInformation
, una instancia que se convierte en el objeto BindingContext
de la vista TableView
. Cada objeto Cell
derivado en TableSection
puede tener enlaces a las propiedades de la clase PersonalInformation
.
Celdas personalizadas
El ejemplo ConditionalCells se expande en EntryForm. La clase ProgrammerInformation
incluye una propiedad booleana que rige la aplicabilidad de dos propiedades adicionales. Para estas dos propiedades adicionales, el programa utiliza un elemento PickerCell
personalizado basado en los ejemplos PickerCell.xaml y PickerCell.xaml.cs de la biblioteca Xamarin.FormsBook.Toolkit.
Aunque las propiedades IsEnabled
de los dos elementos PickerCell
se enlazan a la propiedad booleana en ProgrammerInformation
, esta técnica no parece funcionar, lo que solicita el ejemplo siguiente.
Secciones condicionales
El ejemplo ConditionalSection coloca los dos elementos que son condicionales en la selección del elemento booleano en un elemento TableSection
independiente. El archivo de código subyacente quita esta sección de la vista TableView
o la agrega de nuevo en función de la propiedad booleana.
Menú TableView
Otro uso de una vista TableView
es un menú. En el ejemplo MenuCommands se muestra un menú que le permite mover un poco el elemento BoxView
alrededor de la pantalla.