Статьи

Windows Phone: Live Shaping (живая фильтрация, группировка и сортировка коллекций)


Для каждого списка элементов, используемого в приложении WPF, представление коллекции фактически создается и используется элементами управления.
Это представление обеспечивает навигацию, фильтрацию, группирование и сортировку из XAML или кода. Это очень мощная функция, которая предлагается разработчикам уже давно в WPF.

В WPF 4.5, как и в WPF 4.0, операции группировки, сортировки и фильтрации выполняются при добавлении элемента в коллекцию или при вызове метода Refresh. Недостатком является то, что если значение свойства элемента, участвующее в одной из этих операций, обновляется, то сортировка / группировка / фильтрация не будет выполнена снова.

В WPF 4.5 появилась функция под названием live shaping, которая формирует представление коллекции в прямом эфире. Давайте копать немного больше этой функции.

Этот пост является десятой частью серии о новых возможностях WPF 4.5 .

Основное поведение

Когда вы используете коллекцию в WPF, это фактически ее представление по умолчанию, которое используется элементами управления. Если вам нужна дополнительная информация, вам следует прочитать сообщение Беа Столльниц об этой функции .

Вы можете получить представление по умолчанию для коллекции, используя статический метод CollectionViewSource с именем GetDefaultView :

var collectionView
     = CollectionViewSource.GetDefaultView(Persons) as ICollectionView;

Интерфейс ICollectionView предоставляет методы, которые позволяют группировать и сортировать данные, описанные в коллекциях GroupDescription и SortDescription соответственно. Группировка может затем использоваться в ItemsControl путем установки свойства GroupStyle. В приведенном ниже фрагменте лица отсортированы и сгруппированы по возрасту:

//Group by age
collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Age"));
 
//sort by age
collectionView.SortDescriptions.Add(new SortDescription("Age",
     ListSortDirection.Ascending));

 Кроме того, предикат может быть определен, чтобы сказать, представлен ли элемент представлением коллекции. В этом примере мы не выставляем детей (до 18 лет):

private void ApplyFilter(ICollectionView collectionView)
{
  //Apply the filter
  collectionView.Filter = IsPersonAccepted;
}
 
//Children are not accepted
public bool IsPersonAccepted(object item)
{
    Person p = item as Person;
    return (p != null) && p.Age > 18;
}

Элементы фильтруются, сортируются и группируются в двух случаях:

  • Когда они добавляются в коллекцию, которая является источником представления.
  • Когда вы вызываете метод Refresh для представления.

Это хорошо, но не охватывает все возможные сценарии. Например, если элемент обновляет значение свойства, для которого выполняется одна из этих операций, обновление не выполняется.

Чтобы проиллюстрировать это, я создал небольшую демонстрацию, которая группирует и сортирует коллекцию людей по возрасту и удаляет группу до 18 лет. Ее можно найти в моей папке Dropbox после регистрации . Кнопка, случайным образом меняет возраст людей. Другой добавляет один год каждому из лиц коллекции. После нескольких нажатий на эти кнопки список заканчивается отображением странных вещей: группы больше не являются репрезентативными, а лица не сортируются. Более того, есть дети (до 18 лет), которые отображаются, а некоторые — нет:
Живая сортировка не включена пример

Это может быть требуемой функцией, так как вы можете захотеть обновить отображение коллекции в режиме реального времени. Если это так, то в WPF 4.5 вам доступно живое формирование!

Живой шейпинг

Эта новая функция является опциональной. Вы можете активировать живую сортировку, живую группировку и живую фильтрацию отдельно, и по умолчанию каждый из них отключен.

Интерфейс ICollectionView остается прежним, и больше ничего не раскрывается. Однако вы должны использовать новый интерфейс с именем ICollectionViewLiveShaping .

Существует три свойства относительно каждой операции (группировка, сортировка, фильтрация):

  • Логическое CanChangeLiveXXX, сообщающее, можно ли активировать динамическое формирование для операции XXX. Это только для чтения. BindingListCollectionView и ListCollectionView всегда возвращают true в это время.
  • Обнуляемый логический IsLiveXXX, сообщающий, активирован ли динамический шейпинг для операции XXX.
  • Коллекция строк LiveXXXProperties, содержащая имена свойств элемента представления относительно операции.

Самый важный, может быть, последний. Когда вы определяете сортировку, группировку или фильтр, включаются некоторые свойства содержащихся элементов. Добавляя имя свойства в коллекцию LiveXXXProperties,
вы сообщаете платформе: при каждом изменении свойства с этим именем необходимо обновить операцию XXX
. Как вы уже догадались, он работает с использованием интерфейса INotifyPropertyChanged. Представление коллекции регистрируется для каждого элемента, добавляемого в коллекцию, и прослушивает изменения в целевых свойствах. Просто как тот !

Процесс активации одной операции:

  1. Убедитесь, что целевой элемент реализует INotifyPropertyChanged;
  2. Получить ICollectionViewLiveShaping;
  3. Проверьте, можно ли активировать операцию XXX;
  4. Добавьте свойство, участвующее в операции XXX, в свойствах LiveXXXProperties;
  5. Активируйте операцию XXX через свойство IsLiveXXX.

 

Я создал несколько помощников для вышеуказанного демонстрационного приложения, которые активируют каждого из них.

Живая сортировка

//In a method before
   ActiveLiveSorting(collectionView, new List<string>("Age");
   // ...
 
private void ActiveLiveSorting(ICollectionView collectionView,
   IList<string> involvedProperties)
{
    var collectionViewLiveShaping = collectionView as ICollectionViewLiveShaping;
    if (collectionViewLiveShaping == null) return;
    if (collectionViewLiveShaping.CanChangeLiveSorting)
    {
        foreach(string propName in involvedProperties)
          collectionViewLiveShaping.LiveSortingProperties.Add(propName);
        collectionViewLiveShaping.IsLiveSorting = true;
    }
}

Живая группировка

 //In a method before
   ActiveLiveGrouping(collectionView, new List<string>("Age");
   // ...
 
private void ActiveLiveGrouping(ICollectionView collectionView,
   IList<string> involvedProperties)
{
    var collectionViewLiveShaping = collectionView as ICollectionViewLiveShaping;
    if (collectionViewLiveShaping == null) return;
    if (collectionViewLiveShaping.CanChangeLiveGrouping)
    {
        foreach(string propName in involvedProperties)
          collectionViewLiveShaping.LiveGroupingProperties.Add(propName);
        collectionViewLiveShaping.IsLiveGrouping = true;
    }
}

Живая фильтрация

  //In a method before
   ActiveLiveFiltering(collectionView, new List<string>("Age");
   // ...
 
private void ActiveLiveFiltering(ICollectionView collectionView,
   IList<string> involvedProperties)
{
    var collectionViewLiveShaping = collectionView as ICollectionViewLiveShaping;
    if (collectionViewLiveShaping == null) return;
    if (collectionViewLiveShaping.CanChangeLiveFiltering)
    {
        foreach(string propName in involvedProperties)
          collectionViewLiveShaping.LiveFilteringProperties.Add(propName);
        collectionViewLiveShaping.IsLiveFiltering = true;
    }
}

Затем вы можете играть сколько угодно с помощью кнопок, группа, сортировка и фильтрация выполняются хорошо.

Примечания стороны

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

Наконец, вам может показаться интересным, что класс ItemsCollection, который используется для хранения списка элементов ItemControls, также реализует интерфейс ICollectionViewLiveShaping.