Аннотация к главе 19. Представления коллекций

Примечание.

Эта книга была опубликована весной 2016 года и с тех пор не обновлялась. Многое в этой книге остается ценным, но некоторые материалы устарели, а некоторые разделы перестали быть полностью верными или полными.

Xamarin.Forms определяет три представления, которые поддерживают коллекции и показывают их элементы:

  • Picker — это относительно короткий список строковых элементов, позволяющий пользователю выбрать один из них.
  • ListView — часто является длинным списком элементов, у которых обычно одинаковые тип и форматирование, здесь также пользователь может выбрать один из них.
  • TableView — это коллекция ячеек (обычно различных типов и вида) для отображения данных или управления вводом пользователя.

Обычно приложения MVVM используют ListView для отображения доступной для выбора коллекции объектов.

Варианты программы с использованием Picker

Picker — хороший выбор, если необходимо разрешить пользователю выбирать вариант из относительно короткого списка элементов string.

Picker и обработка событий

В примере PickerDemo показано, как использовать XAML для заданияTitlePickerсвойства и добавления 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— добавляет представление в ImageTextCell
  • EntryCell — содержит Entry представление с помощью Label
  • SwitchCell — содержит объект Switch с a Label
  • 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 выполняет привязку BindingContextStackLayout к свойству SelectedItem представления ListView. Это позволяет программе показывать подробные сведения о выбранном учащемся.

Контекстные меню

Ячейка может определить контекстное меню, реализованное для конкретной платформы. Чтобы создать это меню, добавьте объекты MenuItem в свойство ContextActions объекта Cell.

MenuItem определяет пять свойств:

Свойства 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 определяет только четыре свойства:

Перечисление 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 по экрану.