Resumen del capítulo 19 Vistas de colecciones

Download SampleDescargar el ejemplo

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 PickerTitlepropiedad y agregar string elementos de Items a la colección. 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 dos Label vistas (conceptualmente hablando)
  • ImageCell — agrega una Image vista de TextCell
  • EntryCell — contiene una Entry vista con un Label
  • SwitchCell — contiene un Switch con un Label
  • ViewCell — puede ser cualquier View (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:

Triple screenshot of Custom Named Color List

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:

Triple screenshot of Student List

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:

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:

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 de BindableObject y define una propiedad Title.

  • TableSectionBase<T> es una clase abstracta que deriva de TableSectionBase e implementa IList<T> y INotifyCollectionChanged.

  • TableSection deriva de TableSectionBase<Cell>.

  • TableRoot deriva de TableSectionBase<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.