Главная » Разработка для Windows Phone 7 » Элементы управления списками Windows Phone 7

0

Остается еще одна базовая категория элементов управления, которую мы до сих пор не обсудили. Это ветка класса ItemsControl, производного от Control. Привожу полную иерархию классов этой ветки:

Object

DependencyObject (абстрактный) UIElement (абстрактный)

FrameworkElement (абстрактный) Control (абстрактный) ItemsControl

Selector (абстрактный) ListBox ComboBox TemplatedItemsControl (универсальный) Panorama Pivot

PivotHeadersControl MapItemsControl

ItemsControl и его производные обеспечивают отображение коллекций элементов. Кроме того, класс Selector (Селектор) и его производные реализуют свойства и логику, позволяющие пользователю выбирать один или более элементов коллекции. (Классы, наследуемые от TemplatedItemsControl (Шаблонный элемент управления списками), будут обсуждаться в следующей главе.)

Наверное, самым известным из элементов управления этого типа является ListBox, который присутствует в средах Windows уже на протяжении 25 лет. Исходный ListBox – это вертикальный список элементов с возможностью прокрутки, по которым можно перемещаться с помощью клавиатуры или мыши. (В Windows Phone 7 это делается посредством сенсорного ввода.) Может быть выбран один или (такая возможность также может быть реализована) несколько элементов. Элемент управления выделяет выбранный элемент и делает его доступным для обработки. ComboBox (Поле со списком) появился несколько позднее, чем ListBox, и назван так, потому что сочетает поле с возможностью редактирования текста и выпадающий ListBox.

ItemsControl не так привычен ветеранам разработки на платформе Windows. Часто он очень похож на ListBox, но не реализует никакой логики выбора. (Он даже не реализует прокрутки, но ее не сложно добавить.) ItemsControl предназначен просто для целей представления. И хотя ItemsControl это не ListBox, он все равно имеет огромное значение в разработке на Silverlight и также пригодится для реализации пользовательской логики выбора.

ItemsControl и все его производные обобщенно называют элементами управления списками – три слова, которые вынесены в название этой главы. Я откладывал обсуждение этого семейства элементов управления до сих пор, потому что практически всегда для формирования визуального представления элементов в элементах управления ItemsControl используется DataTemplate. ItemsControl и DataTemplate буквально созданы друг для друга.

Элементы управления списками и деревья визуальных элементов

Существует три основных способа заполнения элемента управления списками: с помощью кода, XAML и привязки данных.

Метод с применением кода продемонстрирован в проекте ItemsControlsFromCode (Заполнение элементов управления списками из кода). Данное приложение предназначено для отображения в альбомном режиме. В каждом из трех столбцов сетки для содержимого создается по экземпляру ItemsControl, ListBox и ComboBox:

Проект Silverlight: ItemsControlsFromCode Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.ColumnDefinitions>

<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />п </Grid.ColumnDefinitions>

<ItemsControl Name="itemsControl" Grid.Column="0" />

<ListBox Name="listBox" Grid.Column="1" />

<ComboBox Name="comboBox" Grid.Column="2" VerticalAlignment="Top" Foreground="Black" />

</Grid>

Я задал для ComboBox пару свойств. При реализации возможности выпадающего списка лучше выравнивать элемент управления по верху ячейки. Также я обнаружил, что шаблон по умолчанию для ComboBox не был скорректирован для телефона, поэтому для отображения элементов было необходимо задать свойство Foreground.

Файл выделенного кода заполняет каждый из этих элементов управления объектами FontFamily:

Проект Silverlight: ItemsControlsFromCode Файл: MainPage.xaml.cs (фрагмент)

public partial class MainPage : PhoneApplicationPage {

public MainPage() {

InitializeComponent();

FillItUp(itemsControl);

FillItUp(listBox);

FillItUp(comboBox);

}

void FillItUp(ItemsControl itemsControl) {

string[] fontFamilies = {

"Arial", "Arial Black", "Calibri", "Comic Sans MS", "Courier New", "Georgia", "Lucida Sans Unicode", "Portable User Interface", "Segoe WP", "Segoe WP Black", "Segoe WP Bold", "Segoe WP Light", "Segoe WP Semibold", "Segoe WP SemiLight", "Tahoma", "Times New Roman", "Trebuchet MS", "Verdana", "Webdings"

foreach (string fontFamily in fontFamilies)

itemsControl.Items.Add(new FontFamily(fontFamily));

Свойство Items (Элементы), определенное ItemsControl, типа ItemCollection (Коллекция элементов), и в него можно поместить практически все что угодно. Если объект, помещаемый в коллекцию, наследуется от FrameworkElement (как Button, например), элемент будет самостоятельно реализовывать свое отображение. В противном случае применяется метод ToString элемента. Очень удобно, что в классе FontFamily есть метод ToString, который обеспечивает вывод на экран имени FontFamily:

Возможно, первое, на что обращаешь внимание в данном приложении – это отсутствие прокрутки в ItemsControl. Если хотите реализовать прокрутку ItemsControl, поместите его в ScrollViewer.

ListBox имеет собственный ScrollViewer. И прокрутка, и выбор элементов осуществляется посредством касания. При выборе элемента он выделяется контрастным цветом, как выделен шрифт Tahoma в данном примере.

ComboBox не раскрывается до тех пор, пока его не коснуться. В случае любого касания этого элемента управления, раскрывается список:

Теперь понятно, почему я задал свойству Foreground значение Black. Сначала я задал в качестве его значения ресурс PhoneBackgroundBrush, но затем обнаружил, что ComboBox использует те же цвета и со светлой темой.

Чтобы соответствовать эстетике Windows Phone 7, ComboBox необходим ControlTemplate. Поэтому я не буду описывать этот элемент управления в данной книге.

Большая часть этой главы посвящена определению шаблонов для элементов управления списками, поэтому полезным будет рассмотреть деревья визуальных элементов этих трех элементов управления, чтобы получить представление об их внутренней архитектуре.

Проект ItemsControlsVisualTrees (Деревья визуальных элементов для элементов управления списками) очень похож на проект ItemsControlsFromCode за исключением того, что в нем вместо ComboBox используется еще один ItemsControl (но этот уже в ScrollViewer), а также имеется несколько кнопок. Этот второй ItemsControl применяется для отображения деревьев визуальных элементов, ассоциированных с первым ItemsControl и ListBox.

Рассмотрим область содержимого, описанную в XAML-файле. Чтобы предоставить ItemsControl, используемому для отображения деревьев визуальных элементов, достаточно пространства по горизонтали, я уменьшил ширину первых двух столбцов:

Проект Silverlight: ItemsControlsVisualTrees Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.ColumnDefinitions>

<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="2*" /> </Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions>

<ItemsControl Name="itemsControl"

Grid.Column="0" Grid.Row="0" />

<Button Content="Dump"

Grid.Column="0" Grid.Row="1" Click="OnItemsControlDumpClick" />

<ListBox Name="listBox"

Grid.Column="1" Grid.Row="0" />

<Button Content="Dump"

Grid.Column="1" Grid.Row="1" Click="OnListBoxDumpClick" />

<ScrollViewer Grid.Column="2" Grid.Row="0" Grid.RowSpan="2">

<ItemsControl Name="dumpTreeItemsControl" /> </ScrollViewer> </Grid>

Файл выделенного кода сначала заполняет первые два элемента управления объектами FontFamily, как это было в предыдущем приложении:

Проект Silverlight: ItemsControlsVisualTrees Файл: MainPage.xaml.cs (фрагмент)

public MainPage() {

InitializeComponent();

FillItUp(itemsControl); FillItUp(listBox);

}

void FillItUp(ItemsControl itemsControl) {

string[] fontFamilies = {

"Arial", "Arial Black", "Calibri", "Comic Sans MS", "Courier New", "Georgia", "Lucida Sans Unicode", "Portable User Interface", "Segoe WP", "Segoe WP Black", "Segoe WP Bold", "Segoe WP Light", "Segoe WP Semibold", "Segoe WP SemiLight", "Tahoma", "Times New Roman", "Trebuchet MS", "Verdana", "Webdings"

foreach (string fontFamily in fontFamilies)

itemsControl.Items.Add(new FontFamily(fontFamily));

}

Этот класс также включает обработчики событий Click для двух кнопок и реагирует на эти события, отображая дерево визуальных элементов соответствующего элемента управления списками:

Проект Silverlight: ItemsControlsVisualTrees Файл: MainPage.xaml.cs (фрагмент)

void 0nItemsControlDumpClick(object sender, RoutedEventArgs args) {

dumpTreeItemsControl.Items.Clear(); DumpVisualTree(itemsControl, 0);

}

void 0nListBoxDumpClick(object sender, RoutedEventArgs args) {

dumpTreeItemsControl.Items.Clear(); DumpVisualTree(listBox, 0);

}

void DumpVisualTree(Dependency0bject parent, int indent) {

TextBlock txtblk = new TextBlock();

txtblk.Text = String.Format("{0}{1}", new string(‘ ‘, 4 * indent),

parent.GetType().Name); dumpTreeItemsControl.Items.Add(txtblk);

int numChildren = VisualTreeHelper.GetChildrenCount(parent);

for (int childIndex = 0; childIndex < numChildren; childIndex++) {

Dependency0bject child = VisualTreeHelper.GetChild(parent, childIndex); DumpVisualTree(child, indent + 1);

}

}

На иллюстрации приложение отображает дерево визуальных элементов ItemsControl:

Полное дерево визуальных элементов потенциально включает несколько шаблонов. Это может обусловливать некоторые сложности, поэтому сделаем небольшой обзор.

В некоторых элементах управления, таких как Slider, ControlTemplate определяет представление всего элемента управления. Стиль темы для Slider определяет ControlTemplate по умолчанию и также можно определить собственный ControlTemplate. Этот ControlTemplate может включать описания других ControlTemplate для ToggleButton и Thumb, образующих Slider.

В производных ContentControl, таких как Button, потенциально имеется два шаблона: ControlTemplate, определяющий визуальный стиль элемента управления, и DataTemplate, описывающий, как в элементе управления формируется визуальное представление объекта, заданного в качестве значения свойства Content.

В производных ItemsControl используются три типа шаблонов: ControlTemplate для визуального стиля элемента управления, ItemsPanelTemplate для панели, которая используется для размещения элементов, и DataTemplate, применяемый к каждому элементу.

Дерево визуальных элементов ItemsControl начинается с ControlTemplate для элемента управления, и ControlTemplate по умолчанию для ItemsControl – просто ItemsPresenter. Это довольно загадочный класс, не определяющий не одного открытого свойства. ItemsPresenter иногда используется как «местозаполнитель» для элемента управления списками в пользовательском ControlTemplate.

Для отображения элементов в элементе управления списками ItemsPresenter всегда использует производный от Panel. В стандартном ItemsControl это StackPanel с вертикальной ориентацией. Эту панель можно заменить, задав свойству ItemsPanel, определенному ItemsControl, значение ItemsPanelTemplate. Как это делается, рассматривается далее в этой главе.

За этим следуют совершенно идентичные фрагменты деревьев визуальных элементов для каждого элемента коллекции, начиная со знакомого элемента под именем ContentPresenter. Если вспомнить предыдущую главу, ContentPresenter является ядром ContentControl. Этот элемент отвечает за размещение производных FrameworkElement; или преобразование объектов, не являющихся производными FrameworkElement, в текст с помощью метода TextString; или за размещение объекта с использованием дерева визуальных элементов, построенного на основании DataTemplate, который задан как значение свойства ContentTemplate. ContentPresenter играет такую же роль в данном случае, но для каждого элемента в отдельности.

В ItemsControl, рассматриваемом в данном приложении, каждый элемент отображается посредством TextBlock, помещенного в Grid с одной ячейкой.

Дерево визуальных элементов ListBox намного более запутанное:

Дерево визуальных элементов начинается с ControlTemplate по умолчанию для ListBox. Данное дерево начинается с ScrollViewer, который является элементом управления, поэтому имеет собственный ControlTemplate и собственное дерево визуальных элементов, начинающееся с Border и заканчивающееся ScrollContentPresenter (Средство отображения содержимого с прокруткой), который функционирует как механизм ScrollViewer. ScrollViewer наследуется от ContentControl, и в рамках ControlTemplate для ListBox значением свойства Content объекта ScrollViewer задается ItemsPresenter – тот же класс, который полностью формирует ControlTemplate по умолчанию для ItemsControl.

В дереве визуальных элементов ItemsControl в ItemsPresenter размещается StackPanel; а в ListBox ItemsPresenter размещается VirtualizingStackPanel (Виртуализирующая стек-панель). Позвольте мне вернуться к этому позднее.

В дереве визуальных элементов ItemsControl каждый элемент – это ContentPresenter (мы познакомились с этим классом в предыдущей главе). В данном случае каждый элемент – это ListBoxItem (Элемент окна списка), который сам по себе наследуется от ContentControl и имеет собственный шаблон и собственный ContentPresenter.

Почему такая разница? Почему у ListBox есть специальный класс под именем ListBoxItem для размещения каждого элемента, а у ItemsControl такого класса нет?

Ответ прост: выбор. Кто-то должен обрабатывать особое отображение выбранного элемента в ListBox и ComboBox, и именно для этой цели существуют классы ListBoxItem и ComboBoxItem (Элемент поля со списком) (который наследуется от ListBoxItem). ListBoxItem наследуется от ContentControl – из дерева визуальных элементов можно видеть, что в своем шаблоне он включает ContentControl, как и Button – но также определяет свойство IsSelected. ListBox знает, что его элементы располагаются в элементах управления ListBoxItem, поэтому он может задать свойство IsSelected для выбранного элемента, что используется шаблоном ListBoxItem для выделения этого элемента.

В системе понятий элементов управления списками ListBoxItem играет роль контейнера для элементов, располагающихся в ListBox. Эти контейнеры ListBoxItem создаются автоматически при добавлении новых элементов в ListBox. Открытый интерфейс для создания и управления этими контейнерами описывается классом ItemsControl. К нему относятся свойство ItemContainerGenerator (Генератор контейнера элементов) и несколько перегружаемых методов для определения альтернативного класса контейнера. Но обсуждение контейнеров выходит за рамки данной книги.

Может возникнуть желание определить для ListBoxItem другой ControlTemplate, например, чтобы изменить способ выделения выбранных элементов. Небольшое беспокойство вызывает то, что созданием и работой с экземплярами ListBoxItem занимается логика контейнера, но, к счастью, создать пользовательский ControlTemplate для ListBoxItem проще, чем кажется. И ListBox, и ComboBox определяют свойство ItemContainerStyle (Стиль контейнера элементов), в качестве значения которого можно задать объект Style, который ListBox применяет к каждому экземпляру ListBoxItem. Безусловно, Style может включать метод Setter для свойства Template. Это простой подход. А если требуется, чтобы ListBox применял пользовательский контейнер класса, унаследованный от ListBoxItem, придется обратиться к логике контейнера-генератора.

Если внимательно посмотреть на эти деревья визуальных элементов – не забывая о том, что в общем случае каждый элемент получит собственное дерево визуальных элементов, определенное DataTemplate – могут возникнуть опасения по поводу производительности. Не допускайте, чтобы DataTemplate был слишком сложным. VirtualizingStackPanel – это еще один класс, который способствует лучшей производительности. Он создает дерево визуальных

элементов для объекта только в момент, когда этот объект должен отображаться. От VirtualizingPanel можно наследовать собственные виртуализирующие панели, но, боюсь, эта тема также выходит за рамки данной книги.

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии