Аннотация к главе 19. Представления коллекций
Примечание.
Эта книга была опубликована весной 2016 года и с тех пор не обновлялась. Многое в этой книге остается ценным, но некоторые материалы устарели, а некоторые разделы перестали быть полностью верными или полными.
Xamarin.Forms определяет три представления, которые поддерживают коллекции и показывают их элементы:
Picker
— это относительно короткий список строковых элементов, позволяющий пользователю выбрать один из них.ListView
— часто является длинным списком элементов, у которых обычно одинаковые тип и форматирование, здесь также пользователь может выбрать один из них.TableView
— это коллекция ячеек (обычно различных типов и вида) для отображения данных или управления вводом пользователя.
Обычно приложения MVVM используют ListView
для отображения доступной для выбора коллекции объектов.
Варианты программы с использованием Picker
Picker
— хороший выбор, если необходимо разрешить пользователю выбирать вариант из относительно короткого списка элементов string
.
Picker и обработка событий
В примере PickerDemo показано, как использовать XAML, чтобы задать свойство Title
для Picker
и добавить элементы string
в коллекцию Items
. Когда пользователь выбирает Picker
, отображаются элементы в коллекции Items
в зависимости от платформы.
Событие SelectedIndexChanged
указывает, что пользователь выбрал элемент. Свойство SelectedIndex
отсчитывается от нуля, затем указывает выбранный элемент. Если элемент не выбран, SelectedIndex
равно –1.
Можно также использовать SelectedIndex
для инициализации выбранного элемента, но это свойство должно быть задано после заполнения коллекции Items
. В XAML это означает, что вы, вероятно, используете элемент свойства для установки SelectedIndex
.
Привязка данных с использованием Picker
Свойство SelectedIndex
поддерживается связываемым свойством, а Items
— нет, поэтому использование привязки данных с Picker
усложняется. Одним из решений является использование Picker
в сочетании с ObjectToIndexConverter
, например из библиотеки Xamarin.FormsBook.Toolkit. Пример PickerBinding демонстрирует, как это работает.
Примечание.
Теперь они Xamarin.FormsPicker
включают ItemsSource
и SelectedItem
свойства, поддерживающие привязку данных. Дополнительную информацию см. в статье о Picker.
Преобразование данных с помощью ListView для просмотра
ListView
— единственный класс, производный от ItemsView<TVisual>
, от которого наследуются свойства ItemsSource
и ItemTemplate
.
Свойство ItemsSource
имеет тип IEnumerable
и значение null
по умолчанию и должно быть явно инициализировано или (зачастую) задано коллекцией путем привязки данных. Элементы в этой коллекции могут быть любого типа.
ListView
определяет свойство SelectedItem
, для которого либо задан один из элементов в коллекции ItemsSource
, либо — null
, если ни один элемент не выбран. ListView
запускает событие ItemSelected
при выборе нового элемента.
Коллекции и выделения
Пример ListViewList заполняет ListView
, указывая 17 значений для Color
в коллекции List<Color>
. Элементы можно выбирать, но по умолчанию они отображаются со своими непривлекательными представлениями ToString
. Несколько примеров в этой главе показывают, как исправить это отображение и сделать его привлекательным при необходимости.
Разделитель строк
На дисплеях устройств iOS и Android тонкая линия разделяет строки. Ее отображение можно контролировать с помощью свойств SeparatorVisibility
и SeparatorColor
. Свойство SeparatorVisibility
имеет тип SeparatorVisibility
— перечисление с двумя членами:
Привязка данных к выбранному элементу
Свойство SelectedItem
поддерживает привязываемое свойство, поэтому оно может быть или источником, или целью привязки данных. Значение свойства по умолчанию BindingMode
равно OneWayToSource
, но обычно оно является целевым объектом двухсторонней привязки данных, особенно в сценариях MVVM. Этот тип привязки показан в примере ListViewArray.
Отличие ObservableCollection
В примере ListViewLogger задается свойство ItemsSource
для ListView
в коллекции List<DateTime>
, а затем в коллекцию последовательно каждую секунду с использованием таймера добавляется новый объект DateTime
.
Но ListView
не обновляется автоматически, так как в коллекции List<T>
нет механизма уведомления, указывающего, когда элементы добавляются в коллекцию или удаляются из нее.
В таких сценариях гораздо лучше использовать класс ObservableCollection<T>
, определенный в пространстве имен System.Collections.ObjectModel
. Этот класс реализует интерфейс INotifyCollectionChanged
и затем вызывает событие CollectionChanged
при добавлении элементов в коллекцию или их удалении из нее, а также при их замене или перемещении в коллекции. Когда ListView
внутренне обнаруживает, что классу, реализующему INotifyCollectionChanged
, задано свойство ItemsSource
, представление присоединяет обработчик к событию CollectionChanged
и обновляет отображение при изменении коллекции.
В примере ObservableLogger показано использование ObservableCollection
.
Шаблоны и ячейки
По умолчанию ListView
показывает элементы в коллекции с помощью метода ToString
каждого элемента. Лучший подход — определить шаблон для отображения элементов.
Чтобы опробовать эту возможность, можно использовать класс NamedColor
из библиотеки Xamarin.FormsBook.Toolkit. Этот класс определяет статическое свойство All
типа IList<NamedColor>
, которое содержит 141 объект NamedColor
, соответствующий открытым полям структуры Color
.
В примере NaiveNamedColorList задается ItemsSource
представления ListView
для свойства NamedColor.All
, но отображаются только полные имена классов объектов NamedColor
.
Для ListView
требуется шаблон отображения этих элементов. В коде можно задать для свойства ItemTemplate
, определяемого ItemsView<TVisual>
, объект DataTemplate
с помощью конструктора DataTemplate
, ссылающегося на производный класс Cell
. У Cell
пять производных:
TextCell
— содержит дваLabel
представления (концептуально говоря)ImageCell
— добавляет представление вImage
TextCell
EntryCell
— содержитEntry
представление с помощьюLabel
SwitchCell
— содержит объектSwitch
с aLabel
ViewCell
— может быть любымView
(вероятно, с детьми)
Затем вызовите SetValue
и SetBinding
для объекта DataTemplate
и свяжите значения со свойствами Cell
или задайте привязки данных в свойствах Cell
, сославшись на свойства элементов в коллекции ItemsSource
. Это показано в примере TextCellListCode.
ListView
отображает каждый элемент, поэтому строится небольшое визуальное дерево на основе шаблона, а между элементом и свойствами элементов в этом визуальном дереве устанавливаются привязки данных. Чтобы понять процесс, установите обработчики для событий ItemAppearing
и ItemDisappearing
представления ListView
или используйте альтернативный конструктор DataTemplate
, который применяет функцию, вызываемую при каждом создании визуального дерева элементов.
TextCellListXaml показывает полностью функционально идентичную программу в XAML. Для тега DataTemplate
устанавливается свойство ItemTemplate
представления ListView
, а затем для TextCell
устанавливается DataTemplate
. Привязки к свойствам элементов в коллекции задаются напрямую свойствам Text
и Detail
для TextCell
.
Пользовательские ячейки
В XAML можно задать ViewCell
для DataTemplate
, а затем определить пользовательское визуальное дерево как свойство View
для ViewCell
. (View
является свойством содержимого ViewCell
, поэтому ViewCell.View
теги не требуются.) Пример CustomNamedColorList демонстрирует этот метод:
Правильно подобрать размеры для всех платформ может быть непросто. Свойство RowHeight
полезно для этого, но в некоторых случаях необходимо прибегнуть к свойству HasUnevenRows
, которое менее эффективно, но принуждает ListView
задавать размер строк. Для iOS и Android необходимо использовать одно из этих двух свойств, чтобы получить строки соответствующего размера.
Группирование элементов ListView
ListView
поддерживает группирование элементов и навигацию между этими группами. Свойство ItemsSource
должно быть задано в коллекцию коллекций: объект, ItemsSource
который должен реализовываться IEnumerable
, и каждый элемент в коллекции также должен реализовываться IEnumerable
. У каждой группы должно быть два свойства: текстовое описание группы и трехбуквенное сокращение.
Класс NamedColorGroup
из библиотеки Xamarin.FormsBook.Toolkit создает семь групп объектов NamedColor
. В примере ColorGroupList показано, как использовать эти группы со свойством IsGroupingEnabled
представления ListView
и значением true
, а также свойства GroupDisplayBinding
и GroupShortNameBinding
, привязанные к свойствам в каждой группе.
Настраиваемые заголовки групп
Можно создавать настраиваемые заголовки для групп ListView
, заменяя свойство GroupDisplayBinding
на GroupHeaderTemplate
, определяющее шаблон для заголовков.
Интерактивность и ListView
Как правило, приложение получает взаимодействие пользователя через ListView
путем присоединения обработчика к событию ItemSelected
или ItemTapped
или путем привязки данных в свойстве SelectedItem
. Но некоторые типы ячеек (EntryCell
и SwitchCell
) также могут взаимодействовать с пользователем, поэтому можно создавать настраиваемые ячейки, чтобы они взаимодействовали с пользователем. InteractiveListView создает 100 экземпляров ColorViewModel
и позволяет пользователю изменять каждый цвет с помощью тройки элементов Slider
. Кроме того, программа использует ColorToContrastColorConverter
из библиотеки Xamarin.FormsBook.Toolkit.
ListView и MVVM
ListView
играет важную роль в сценариях MVVM. Если во ViewModel существует коллекция IEnumerable
, она зачастую привязана к ListView
. Кроме того, элементы в коллекции часто реализуют INotifyPropertyChanged
для привязки со свойствами в шаблоне.
Коллекция ViewModel
Чтобы изучить эту возможность, библиотека SchoolOfFineArts создает несколько классов на основе файла данных XML и изображений вымышленных учащихся в вымышленном учебном заведении.
Класс Student
является производным от ViewModelBase
. Класс StudentBody
представляет собой коллекцию объектов Student
, а также является производным от ViewModelBase
. SchoolViewModel
скачивает файл XML и собирает все объекты.
Программа StudentList использует ImageCell
для отображения данных учащихся и их изображений в ListView
:
В примере ListViewHeader добавляется свойство Header
, но оно отображается только в Android.
Выделение и контекст привязок
Программа SelectedStudentDetail выполняет привязку BindingContext
StackLayout
к свойству SelectedItem
представления ListView
. Это позволяет программе показывать подробные сведения о выбранном учащемся.
Контекстные меню
Ячейка может определить контекстное меню, реализованное для конкретной платформы. Чтобы создать это меню, добавьте объекты MenuItem
в свойство ContextActions
объекта Cell
.
MenuItem
определяет пять свойств:
Text
типаstring
Icon
типаFileImageSource
IsDestructive
типаbool
Command
типаICommand
CommandParameter
типаobject
Свойства Command
и CommandParameter
подразумевают, что ViewModel для каждого элемента содержит методы, чтобы выполнять нужные команды меню. В сценариях, где не используется MVVM, MenuItem
также определяет событие Clicked
.
CellContextMenu демонстрирует этот метод. Свойство Command
каждого MenuItem
привязано к свойству типа ICommand
в классе Student
. Задайте для свойства IsDestructive
значение true
для MenuItem
, это удалит выбранный объект.
Разные визуальные элементы
Иногда может потребоваться, чтобы было несколько вариантов визуальных объектов элементов в ListView
в зависимости от свойства. Например, если среднее значение оценок учащегося снижается ниже 2,0, то в примере ColorCodedStudents имя учащегося будет отображаться красным цветом.
Для этого используется преобразователь величин привязки ThresholdToObjectConverter
из библиотеки Xamarin.FormsBook.Toolkit.
Обновление содержимого
ListView
поддерживает жест развертывания для обновления данных. Для этого программа должна задать для свойства IsPullToRefresh
значение true
. ListView
реагирует на жест развертывания, устанавливая для свойства IsRefreshing
значение true
, а также вызывая событие Refreshing
и (для сценариев MVVM) метод Execute
свойства RefreshCommand
.
Затем код, обрабатывающий событие Refresh
, или команда RefreshCommand
, возможно, обновит данные, отображаемые ListView
, и возвратит IsRefreshing
значение false
.
В примере RssFeed демонстрируется использование RssFeedViewModel
, реализующей свойства RefreshCommand
и IsRefreshing
для привязки данных.
Представление TableView и его намерения
Если ListView
обычно показывает несколько экземпляров одного типа, то TableView
, как правило, предоставляет пользовательский интерфейс для нескольких свойств различных типов. Каждый элемент связан с собственным производным Cell
для отображения свойства или предоставления ему пользовательского интерфейса.
Свойства и иерархии
TableView
определяет только четыре свойства:
Intent
типаTableIntent
— перечисление.Root
с типомTableRoot
— это свойство содержимогоTableView
.RowHeight
типаint
HasUnevenRows
типаbool
Перечисление TableIntent
указывает, как предполагается использовать TableView
.
Эти члены также предлагают некоторые варианты использования TableView
.
При определении таблицы участвуют и другие классы:
TableSectionBase
— это абстрактный класс, производный отBindableObject
и определяющий свойствоTitle
.TableSectionBase<T>
— это абстрактный класс, производный отTableSectionBase
и реализующийIList<T>
иINotifyCollectionChanged
.TableSection
происходит отTableSectionBase<Cell>
.TableRoot
происходит отTableSectionBase<TableSection>
.
У TableView
есть свойство Root
, для которого задан объект TableRoot
, представляющий собой коллекцию объектов TableSection
, каждый из которых является коллекцией объектов Cell
. Таблица состоит из нескольких разделов, и в каждом разделе содержится несколько ячеек. Заголовок может быть у самой таблицы, а также у каждого раздела. TableView
использует производные от Cell
, но не использует DataTemplate
.
Простая форма
Пример EntryForm определяет модель представления PersonalInformation
, экземпляр которой превращается в BindingContext
представления TableView
. Каждый производный объект Cell
в своем разделе TableSection
может иметь привязки к свойствам класса PersonalInformation
.
Пользовательские ячейки
В примере ConditionalCells раскрывается EntryForm. Класс ProgrammerInformation
включает в себя логическое свойство, которое регулирует применимость двух дополнительных свойств. Для этих двух дополнительных свойств программа использует пользовательский элемент PickerCell
на основе PickerCell.xamlL и PickerCell.xaml.cs из библиотеки Xamarin.FormsBook.Toolkit.
Хотя свойства IsEnabled
двух элементов PickerCell
и привязаны к логическому свойству в ProgrammerInformation
, этот прием не работает, поэтому предлагаем следующий пример.
Условные разделы
В примере ConditionalSection два элемента, которые являются условными при выборе логического элемента, помещены в отдельный раздел TableSection
. Файл кода программной части удаляет этот раздел из TableView
или добавляет его обратно на основе логического свойства.
Меню TableView
Другим применением TableView
является меню. В примере MenuCommands показано меню, позволяющее перемещать маленькое представление BoxView
по экрану.