Вчера я изучил набор инструментов Silverlight для Windows Phone и рассмотрел некоторые элементы управления, включенные в текущую сборку. LongListSelector — один из элементов управления, который может быть немного необычным в настройке и работе — он позволяет группировать элементы внутри списка. С помощью обычного ListBox вы можете отображать данные, но нет возможности напрямую перейти к набору элементов и нет возможности сгруппировать элементы.
Для начала добавьте ссылку на библиотеку Microsoft.Phone.Controls.Toolkit, которая входит в комплект Silverlight Toolkit. Если вы не уверены, где находится библиотека, ознакомьтесь с моей предыдущей статьей, чтобы получить основную информацию о дистрибутиве. Кроме того, добавьте ссылку на библиотеку, добавив дополнительную ссылку на пространство имен XML в теге страницы:
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
После этого вы можете создать экземпляр элемента управления на самой странице или в любом другом контейнере, который поддерживает элемент управления для установки в качестве содержимого.
<toolkit:LongListSelector x:Name="LongList">
</toolkit:LongListSelector>
Но сейчас сам список — не что иное, как пустой контроль. Чтобы это исправить, прежде всего вам нужно определить несколько шаблонов, которые будут показывать, как именно данные будут разделяться в списке, используя заголовок списка, заголовки групп, заголовки элементов группы (используемые для переключения между группами) и шаблон элемента ,
В словарь ресурсов на основе страниц добавьте следующие шаблоны данных:
<DataTemplate x:Key="GroupHeader">
<Border Background="{StaticResource PhoneAccentBrush}" Margin="{StaticResource PhoneTouchTargetOverhang}" Padding="{StaticResource PhoneTouchTargetOverhang}">
<TextBlock Text="{Binding Key}"/>
</Border>
</DataTemplate>
<DataTemplate x:Key="GroupItem">
<Border Background="{StaticResource PhoneAccentBrush}" Margin="{StaticResource PhoneTouchTargetOverhang}" Padding="{StaticResource PhoneTouchTargetOverhang}">
<TextBlock Text="{Binding Key}" Style="{StaticResource PhoneTextLargeStyle}"/>
</Border>
</DataTemplate>
<DataTemplate x:Key="ListHeader">
<TextBlock Text="Header" Style="{StaticResource PhoneTextTitle1Style}"/>
</DataTemplate>
<DataTemplate x:Key="ItemTmpl">
<Grid>
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
</DataTemplate>
Эти шаблоны очень похожи на те, которые используются в образце, представленном на веб-сайте Silverlight Toolkit . Я решил использовать их здесь, поскольку они представляют собой лучший пример совместимости пользовательского интерфейса с существующими элементами Windows Phone. Но здесь важен не внешний вид.
Первым шаблоном является GroupHeader — он используется для разделения контента и используется для представления набора элементов, сгруппированных по общему свойству. Если вы посмотрите на привязку, она привязана к свойству Key, которое зависит от ItemsSource, установленного в главном списке.
GroupItem используется для представления того же заголовка группы, но в контексте списка переходов, который появляется, когда пользователь касается фактического заголовка группы (таким образом, можно переходить к различным категориям элементов). Он связан с тем же свойством Key, что и каждый из заголовков группы.
ListHeader отображается в верхней части списка, когда он загружен. Он не перемещается вместе со списком, поскольку он прокручивается, и он также не связан ни с какой группой, поэтому он не будет отображаться над заголовком группы, кроме первого. Я оставил этот объект неограниченным, но вы, безусловно, можете связать его с именем суперкатегории (переданным где-то в списке или в отдельном свойстве).
ItemTmpl представляет внешний вид каждого элемента, который попадает в определенную категорию. В моем случае я использую только элемент управления TextBlock, однако вы можете добавить гораздо более богатый набор (например, включить изображения и / или несколько элементов управления). В настоящее время оно связано со свойством Title, которое является частью пользовательского объекта, который я передаю, но я доберусь до него позже.
Теперь мне нужно привязать список к коллекции элементов, которые различаются по категориям. И здесь интересный процесс. Прежде всего, я собираюсь создать собственный класс, который будет представлять объект Item:
public class Item
{
public string Title { get; set; }
public string Content { get; set; }
}
Это довольно просто — два строковых свойства, которые можно установить и получить. Свойство Content в моем случае будет ключом группировки. Но пока нет фактического списка, который можно связать.
Итак, когда моя главная страница инициализируется, я также создаю список Item и наполняю его образцом содержимого:
List<Item> mainItem = new List<Item>();
for (int i = 0; i < 20; i++)
{
mainItem.Add(new Item() { Content = "Category A", Title = "Sample " + i.ToString() });
mainItem.Add(new Item() { Content = "Category B", Title = "Sample B" + i.ToString() });
mainItem.Add(new Item() { Content = "Category C", Title = "Sample C" + i.ToString() });
}
Но здесь есть другая проблема — элементы не сгруппированы. Действительно, они разделены на элементы, которые имеют разные значения свойства Content (и есть 20 элементов, для которых это свойство установлено на одно и то же значение), но как таковые — групп нет.
Прямо сейчас можно использовать интерфейс IGrouping , и я получаю его экземпляр для каждого объекта, установленного через LINQ:
var selected = from c in mainItem group c by c.Content into n select new GroupingLayer<string,Item>(n);
Но посмотрите, что здесь — вместо того, чтобы использовать IGrouping с теми же общими параметрами, я использую GroupingLayer. Это реализация интерфейса IGrouping, который предоставляет свойство Key, используемое для установки имен групп. Если бы я пропустил экземпляр IGrouping, я бы не смог привязаться к свойству Key, поскольку оно является внутренним. GroupingLayer — это слой абстракции, поверх которого:
public class GroupingLayer<TKey, TElement> : IGrouping<TKey, TElement>
{
private readonly IGrouping<TKey, TElement> grouping;
public GroupingLayer(IGrouping<TKey, TElement> unit)
{
grouping = unit;
}
public TKey Key
{
get { return grouping.Key; }
}
public IEnumerator<TElement> GetEnumerator()
{
return grouping.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return grouping.GetEnumerator();
}
}
Поэтому после выполнения запроса LINQ вы получаете экземпляр IEnumerable <GroupingLayer <string, Item >>. Заголовок группы будет автоматически связываться со строковым значением, а каждый сгруппированный элемент будет связан с экземпляром Item, хранящимся в объекте.
Теперь вы можете привязать список к элементу управления:
LongList.ItemsSource = selected;
И, конечно же, элементы будут отображаться в списке: