Статьи

Использование элемента управления LongListSelector в Windows Phone 7

Вчера я изучил набор инструментов 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;

И, конечно же, элементы будут отображаться в списке: