Summary of Chapter 19. Collection views
Note
This book was published in the spring of 2016, and has not been updated since then. There is much in the book that remains valuable, but some of the material is outdated, and some topics are no longer entirely correct or complete.
Xamarin.Forms defines three views that maintain collections and display their elements:
Picker
is a relatively short list of string items that allows the user to choose oneListView
is often a long list of items usually of the same type and formatting, also allowing the user to choose oneTableView
is a collection of cells (usually of various types and appearances) to display data or manage user input
It is common for MVVM applications to use the ListView
to display a selectable collection of objects.
Program options with Picker
The Picker
is a good choice when you need to allow the user to choose an option from among a relatively short list of string
items.
The Picker and event handling
The PickerDemo sample demonstrates how to use XAML to set the Picker
Title
property and add string
items to the Items
collection. When the user selects the Picker
, it displays the items in the Items
collection in a platform-dependent manner.
The SelectedIndexChanged
event indicates when the user has selected an item. The zero-based SelectedIndex
property then indicates the selected item. If no item is selected, SelectedIndex
equals –1.
You can also use SelectedIndex
to initialize the selected item, but it must be set after the Items
collection is filled. In XAML, this means that you'll probably use a property element to set SelectedIndex
.
Data binding the Picker
The SelectedIndex
property is backed by a bindable property but Items
is not, so using data binding with a Picker
is difficult. One solution is to use the Picker
in combination with an ObjectToIndexConverter
such as the one in the Xamarin.FormsBook.Toolkit library. The PickerBinding demonstrates how this works.
Note
The Xamarin.Forms Picker
now includes ItemsSource
and SelectedItem
properties that support data binding. See Picker.
Rendering data with ListView
The ListView
is the only class that derives from ItemsView<TVisual>
from which it inherits the ItemsSource
and ItemTemplate
properties.
ItemsSource
is of type IEnumerable
but it is null
by default and must be explicitly initialized or (more commonly) set to a collection through a data binding. The items in this collection can be of any type.
ListView
defines a SelectedItem
property that is either set to one of the items in the ItemsSource
collection or null
if no item is selected. ListView
fires the ItemSelected
event when a new item is selected.
Collections and selections
The ListViewList sample fills a ListView
with 17 Color
values in a List<Color>
collection. The items are selectable but by default they are displayed with their unattractive ToString
representations. Several examples in this chapter show how to fix that display and make it as attractive as desired.
The row separator
On iOS and Android displays, a thin line separates the rows. You can control this with the SeparatorVisibility
and SeparatorColor
properties. SeparatorVisibility
property is of type SeparatorVisibility
, an enumeration with two members:
Data binding the selected item
The SelectedItem
property is backed by a bindable property, so it can be either the source or target of a data binding. Its default BindingMode
is OneWayToSource
, but generally it is the target of a two-way data binding, particularly in MVVM scenarios. The ListViewArray sample demonstrates this type of binding.
The ObservableCollection difference
The ListViewLogger sample sets the ItemsSource
property of a ListView
to a List<DateTime>
collection and then progressively adds a new DateTime
object to the collection every second using a timer.
However, the ListView
doesn't automatically update itself because the List<T>
collection doesn't have a notification mechanism to indicate when items are added to or removed from the collection.
A much better class to use in such scenarios is ObservableCollection<T>
defined in the System.Collections.ObjectModel
namespace. This class implements the INotifyCollectionChanged
interface and consequently fires a CollectionChanged
event when items are added to or removed from the collection, or when they are replaced or moved within the collection. When the ListView
internally detects that a class implementing INotifyCollectionChanged
has been set to its ItemsSource
property, it attaches a handler to the CollectionChanged
event and updates its display when the collection changes.
The
ObservableLogger sample demonstrates the use of ObservableCollection
.
Templates and cells
By default, a ListView
displays items in its collection using each item's ToString
method. A better approach involves defining a template to display the items.
To experiment with this feature, you can use the NamedColor
class in the Xamarin.FormsBook.Toolkit library. This class defines a static All
property of type IList<NamedColor>
that contains 141 NamedColor
objects corresponding to the public fields of the Color
structure.
The NaiveNamedColorList sample sets the ItemsSource
of a ListView
to this NamedColor.All
property, but only the fully-qualified class names of the NamedColor
objects are displayed.
ListView
needs a template to display these items. In code, you can set the ItemTemplate
property defined by ItemsView<TVisual>
to a DataTemplate
object using the DataTemplate
constructor that references a derivative of the Cell
class. Cell
has five derivatives:
TextCell
— contains twoLabel
views (conceptually speaking)ImageCell
— adds anImage
view toTextCell
EntryCell
— contains anEntry
view with aLabel
SwitchCell
— contains aSwitch
with aLabel
ViewCell
— can be anyView
(likely with children)
Then call SetValue
and SetBinding
on the DataTemplate
object to associate values with the Cell
properties, or to set data bindings on the Cell
properties referencing properties of the items in the ItemsSource
collection. This is demonstrated in the TextCellListCode sample.
As each item is displayed by the ListView
, a small visual tree is constructed from the template, and data bindings are established between the item and the properties of the elements in this visual tree. You can get an idea of this process by installing handlers for the ItemAppearing
and ItemDisappearing
events of the ListView
, or by using an alternative DataTemplate
constructor that uses a function that is called each time an item's visual tree must be created.
The TextCellListXaml shows a functionally identical program entirely in XAML. A DataTemplate
tag is set to the ItemTemplate
property of the ListView
, and then the TextCell
is set to the DataTemplate
. Bindings to properties of the items in the collection are set directly on the Text
and Detail
properties of the TextCell
.
Custom cells
In XAML it is possible to set a ViewCell
to the DataTemplate
and then define a custom visual tree as the View
property of ViewCell
. (View
is the content property of ViewCell
so the ViewCell.View
tags aren't required.) The CustomNamedColorList sample demonstrates this technique:
Getting the sizing right for all the platforms can be tricky. The RowHeight
property is useful but in some cases you'll want to resort to the HasUnevenRows
property, which is less efficient but forces the ListView
to size the rows. For iOS and Android, you must use one of these two properties to get proper row sizing.
Grouping the ListView items
ListView
supports the grouping of items and navigating among those groups. The ItemsSource
property must be set to a collection of collections: The object that ItemsSource
is set to must implement IEnumerable
, and each item in the collection must also implement IEnumerable
. Each group should include two properties: a text description of the group and a three-letter abbreviation.
The NamedColorGroup
class in the Xamarin.FormsBook.Toolkit library creates seven groups of NamedColor
objects. The ColorGroupList sample shows how to use these groups with the IsGroupingEnabled
property of ListView
set to true
, and the GroupDisplayBinding
and GroupShortNameBinding
properties bound to properties in each group.
Custom group headers
It's possible to create custom headers for the ListView
groups by replacing the GroupDisplayBinding
property with the GroupHeaderTemplate
defining a template for the headers.
ListView and interactivity
Generally an application obtains user interaction with a ListView
by attaching a handler to the ItemSelected
or ItemTapped
event, or by setting a data binding on the SelectedItem
property. But some cell types (EntryCell
and SwitchCell
) allow user interaction, and it's also possible to create custom cells that themselves interact with the user. The
InteractiveListView creates 100 instances of ColorViewModel
and allows the user to change each color using a trio of Slider
elements. The program also makes use of the ColorToContrastColorConverter
in the Xamarin.FormsBook.Toolkit.
ListView and MVVM
ListView
plays a big role in MVVM scenarios. Whenever an IEnumerable
collection exists in a ViewModel, it is often bound to a ListView
. Also, the items in the collection often implement INotifyPropertyChanged
to bind with properties in a template.
A collection of ViewModels
To explore this, the SchoolOfFineArts library creates several classes based on an XML data file and images of fictitious students at this fictitious school.
The Student
class derives from ViewModelBase
. The StudentBody
class is a collection of Student
objects and also derives from ViewModelBase
. The SchoolViewModel
downloads the XML file and assembles all the objects.
The StudentList program uses an ImageCell
to display the students and their images in a ListView
:
The ListViewHeader sample adds a Header
property but it only shows up on Android.
Selection and the binding context
The SelectedStudentDetail program binds the BindingContext
of a StackLayout
to the SelectedItem
property of the ListView
. This allows the program to display detailed information about the selected student.
Context menus
A cell can define a context menu that is implemented in a platform-specific manner. To create this menu, add MenuItem
objects to the ContextActions
property of the Cell
.
MenuItem
defines five properties:
Text
of typestring
Icon
of typeFileImageSource
IsDestructive
of typebool
Command
of typeICommand
CommandParameter
of typeobject
The Command
and CommandParameter
properties imply that the ViewModel for each item contains methods to carry out the desired menu commands. In non-MVVM scenarios, MenuItem
also defines a Clicked
event.
The CellContextMenu demonstrates this technique. The Command
property of each MenuItem
is bound to a property of type ICommand
in the Student
class. Set the IsDestructive
property to true
for a MenuItem
that removes or deletes the selected object.
Varying the visuals
Sometimes you'll want slight variations in the visuals of the items in the ListView
based on a property. For example, when a student's grade-point average falls below 2.0, the
ColorCodedStudents sample displays that student's name in red.
This is accomplished through use of a binding value converter, ThresholdToObjectConverter
, in the Xamarin.FormsBook.Toolkit library.
Refreshing the content
The ListView
supports a pull-down gesture for refreshing its data. The program must set the IsPullToRefresh
property to true
to enable this. The ListView
responds to the pull-down gesture by setting its IsRefreshing
property to true
, and by raising the Refreshing
event and (for MVVM scenarios) calling the Execute
method of its RefreshCommand
property.
Code handling the Refresh
event or the RefreshCommand
then possibly updates the data displayed by the ListView
and sets IsRefreshing
back to false
.
The RssFeed sample demonstrates using an RssFeedViewModel
that implements RefreshCommand
and IsRefreshing
properties for data binding.
The TableView and its intents
While the ListView
generally displays multiple instances of the same type, the TableView
is generally focused on providing a user interface for multiple properties of various types. Each item is associated with its own Cell
derivative for displaying the property or providing a user interface to it.
Properties and hierarchies
TableView
defines only four properties:
Intent
of typeTableIntent
, an enumerationRoot
of typeTableRoot
, the content property ofTableView
RowHeight
of typeint
HasUnevenRows
of typebool
The TableIntent
enumeration indicates how you intend to use the TableView
:
These members also suggest some uses for the TableView
.
Several other classes are involved in defining a table:
TableSectionBase
is an abstract class that derives fromBindableObject
and defines aTitle
propertyTableSectionBase<T>
is an abstract class that derives fromTableSectionBase
and implementsIList<T>
andINotifyCollectionChanged
TableSection
derives fromTableSectionBase<Cell>
TableRoot
derives fromTableSectionBase<TableSection>
In short, TableView
has a Root
property that you set to a TableRoot
object, which is a collection of TableSection
objects, each of which is a collection of Cell
objects. A table has multiple sections, and each section has multiple cells. The table itself can have a title, and each section can have a title. Although TableView
makes use of Cell
derivatives, it does not make use of DataTemplate
.
A prosaic form
The EntryForm sample defines a PersonalInformation
view model, an instance of which becomes the BindingContext
of the TableView
. Each Cell
derivative in its TableSection
can then have bindings to properties of the PersonalInformation
class.
Custom cells
The ConditionalCells sample expands on EntryForm. The ProgrammerInformation
class includes a Boolean property that governs the applicability of two additional properties. For these two additional properties, the program uses a custom PickerCell
based on a PickerCell.xaml and PickerCell.xaml.cs in the Xamarin.FormsBook.Toolkit library.
Although the IsEnabled
properties of the two PickerCell
elements are bound to the Boolean property in ProgrammerInformation
, this technique does not seem to work, which prompts the next sample.
Conditional sections
The ConditionalSection sample puts the two items that are conditional on the selection of the Boolean item in a separate TableSection
. The code-behind file removes this section from the TableView
or adds it back based on the Boolean property.
A TableView menu
Another use of a TableView
is a menu. The MenuCommands sample demonstrates a menu that lets you move a little BoxView
around the screen.