Автор Тема: Data Binding. Start #2 Sort, Filter and grouping  (Прочитано 6367 раз)

Sergey

  • Administrator
  • Newbie
  • *****
  • Сообщений: 14
    • Просмотр профиля
Data Binding. Start #2 Sort, Filter and grouping
« : Декабрь 27, 2012, 04:27:39 pm »
Продемонстрируем возможности сортировки в связанном списке на простом примере.

Будем работать с коллекцией студентов, отображаемых в ListBox с помощью Binding.
Заметим, связывание ListBox с коллекцией возможно двумя способами:
  • Назначением коллекции в коде через ItemsSource или DataContext (см. пример Data Binding. Start #1)
  • Назначением коллекции в качестве источника данных непосредственно в XAML. Интересно подметить, что в этом случае за кулисами (без нашего участия) создается экземпляр класса, поставляющий данные для ListBox. Мы лишь должны описать эту работу в XAML.
В рамках данного примера продемонстрируем второй способ.
Обычно создается класс, поставляющий данные. Лучше его расположить в отдельном проекте (рядом с другими классами, работающими на приложение и не имеющими прямого отношения к интерфейсу). Тип проекта может быть либо ClassLibrary, либо WPF User Control Library (в некоторых интересных случаях). Код класса студента очень прост. Кроме имени мы добавили сюда еще и возраст.
Код: vb.net
  1. Public Class StudentClass
  2.  
  3.     Public Property Age As Integer
  4.     Public Property StudentName As String = String.Empty
  5.  
  6.     Public Sub New(StudentName As String, _
  7.                    StudentAge As String)
  8.         Me.StudentName = StudentName
  9.         Me.Age = StudentAge
  10.     End Sub
  11.  
  12.     Public Sub New()
  13.  
  14.     End Sub
  15. End Class
  16.  
Теперь необходимо в главном проекте указать ссылку на библиотеку классов, в которой располагается наш класс студента. Далее, создадим класс, который будет поставлять студентов. Именно, коллекцию студентов. Использование в качестве коллекции ObservableCollection не случайно. Для особо пытливых скажем, что она (эта коллекция) имеет встроенное уведомление об изменении). Если не очень ясно - забудем об этих "глупостях". Код класса - поставщика студентов имеет вид:
Код: vb.net
  1. Imports System.Collections.ObjectModel
  2.  
  3. Public Class StudentCollectionClass
  4.  
  5.     Private _StudentsGroup As ObservableCollection(Of StudentClass)
  6.     Public Property StudentsGroup As ObservableCollection(Of StudentClass)
  7.         Get
  8.             Return _StudentsGroup
  9.         End Get
  10.         Set(value As ObservableCollection(Of StudentClass))
  11.             _StudentsGroup = value
  12.         End Set
  13.     End Property
  14.  
  15.     Public Sub GetStudentsGroup()
  16.  
  17.         Dim NewFormedCollection = New ObservableCollection(Of StudentClass)
  18.         NewFormedCollection.Add(New StudentClass() With {.StudentName = "Nicola", .Age = "39"})
  19.         NewFormedCollection.Add(New StudentClass() With {.StudentName = "Peter", .Age = "43"})
  20.         NewFormedCollection.Add(New StudentClass() With {.StudentName = "Sergey", .Age = "24"})
  21.         NewFormedCollection.Add(New StudentClass() With {.StudentName = "Jan", .Age = "34"})
  22.         NewFormedCollection.Add(New StudentClass() With {.StudentName = "Jeorge", .Age = "18"})
  23.  
  24.         For i As Integer = 0 To 5
  25.             NewFormedCollection.Add(New StudentClass() With {.StudentName = "Boris" & i.ToString,
  26.                                                              .Age = "18"})
  27.         Next
  28.  
  29.         Me.StudentsGroup = NewFormedCollection
  30.     End Sub
  31.  
  32.     Public Sub New()
  33.         Me.GetStudentsGroup()
  34.     End Sub
  35.  
  36. End Class
  37.  

Код разметки главного окна имеет вид:
Код: XML
  1. <Window x:Class="MainWindow"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:classes="clr-namespace:ClassLibrary;assembly=ClassLibrary"
  5.    Title="MainWindow" Height="350" Width="525">
  6.     <Window.Resources>
  7.         <classes:StudentCollectionClass x:Key="StudentCollectionInstance"/>
  8.     </Window.Resources>
  9.     <Grid>
  10.         <ListBox Background="LightGreen"
  11.                 x:Name="ListBoxStudentsName"
  12.                 VerticalAlignment="Top"
  13.                 Width="100"
  14.                 ItemsSource="{Binding Source={StaticResource StudentCollectionInstance}, Path=StudentsGroup}"
  15.                 DisplayMemberPath="StudentName"
  16.                 MinWidth="200"/>
  17.     </Grid>
  18. </Window>
  19.  

Два слова об особенностях. В классе коллекции студентов просто обязан присутствовать пустой конструктор, - иначе XAML не поймет что и каким образом создавать. На практике, мы попросту не увидим ссылку в подсказках студии на возможный класс:
<classes:StudentCollectionClass x:Key="StudentCollectionInstance"/>. Это - общее правило использования статических ресурсов (внешних классов) в XAML.
Получаем следующее:


Приступим к простейшей сортировке. Чтобы видеть отчетливее результат наших действий, поместим кнопку в окне, по которой будем инициировать сортировку по именам студентов. Добавим к кнопке следующий обработчик события:
Код: vb.net
  1.  Private Sub BtnSort_Click(sender As Object, e As RoutedEventArgs) Handles BtnSort.Click
  2.         Dim CurrentViewOfStudents As ICollectionView = _
  3.            CollectionViewSource.GetDefaultView(ListBoxStudentsName.ItemsSource)
  4.         ' ========================
  5.         CurrentViewOfStudents.SortDescriptions.Add(New SortDescription("StudentName", _
  6.                                                        ListSortDirection.Ascending))
  7.         ' ========================
  8.     End Sub
  9.  
Потребуется также подключение библиотеки Imports System.ComponentModel
Теперь, по нажатию кнопки, наш список сортируется по именам студентов.
« Последнее редактирование: Декабрь 27, 2012, 06:18:06 pm от Sergey »

Sergey

  • Administrator
  • Newbie
  • *****
  • Сообщений: 14
    • Просмотр профиля
Data Binding. Start #2 Sort, Filter and grouping
« Ответ #1 : Декабрь 27, 2012, 04:57:08 pm »
Интересно, а как сортировать по возрасту?

Изменим в классе StudentCollectionClass - поставщике коллекции, код добавления новых студентов в цикле. Это совсем не обязательный шаг - просто для того, чтобы было больше студентов с отличающимися возрастами.
Код: vb.net
  1.  For i As Integer = 0 To 5
  2.             NewFormedCollection.Add(New StudentClass() With
  3.                        {.StudentName = "Boris" & i.ToString,
  4.                          .Age = (18 + i).ToString})
  5. Next
  6.  
В обработчике события кнопки Sort добавим новое описание сортировки и закомментируем "старое":
Код: vb.net
  1. Private Sub BtnSort_Click(sender As Object, e As RoutedEventArgs) Handles BtnSort.Click
  2.         Dim CurrentViewOfStudents As ICollectionView = _
  3.            CollectionViewSource.GetDefaultView(ListBoxStudentsName.ItemsSource)
  4.         ' ========================
  5.         'CurrentViewOfStudents.SortDescriptions.Add(New SortDescription("StudentName", _
  6.         '                                               ListSortDirection.Ascending))
  7.         CurrentViewOfStudents.SortDescriptions.Add(New SortDescription("Age", _
  8.                                                        ListSortDirection.Ascending))
  9.         ' ========================
  10.     End Sub
  11.  
В коде разметки главного окна изменим отображаемое свойство элементов списка так:
Код: XML
  1. DisplayMemberPath="Age"
После нажатия кнопки сортировки получаем результат:
« Последнее редактирование: Декабрь 27, 2012, 04:58:48 pm от Sergey »

Sergey

  • Administrator
  • Newbie
  • *****
  • Сообщений: 14
    • Просмотр профиля
Data Binding. Start #2 Sort, Filter and grouping
« Ответ #2 : Декабрь 27, 2012, 06:17:32 pm »
Как провести сортировку "одновременно" - по двум свойствам? И как отображать сразу два свойства?

Изменим разметку главного окна, окультурив контейнеры (Grid, добавим StackPanel) и добавив вторую кнопку сортировки:
Код: XML
  1. <Window
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:classes="clr-namespace:ClassLibrary;assembly=ClassLibrary"
  5.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
  6.    Title="MainWindow" d:DesignWidth="244.5" d:DesignHeight="318">
  7.     <Window.Resources>
  8.         <classes:StudentCollectionClass x:Key="StudentCollectionInstance"/>
  9.     </Window.Resources>
  10.     <Grid>
  11.         <Grid.ColumnDefinitions>
  12.             <ColumnDefinition Width="Auto"/>
  13.             <ColumnDefinition/>
  14.         </Grid.ColumnDefinitions>
  15.         <ListView Background="LightGreen"
  16.                         x:Name="ListBoxStudentsName"
  17.                         ItemsSource="{Binding StudentsGroup,
  18.                          Source={StaticResource StudentCollectionInstance}}"
  19.                         MinWidth="200" Grid.Column="1">
  20.             <ListView.View>
  21.                 <GridView>
  22.                     <!-- First Column -->
  23.                     <GridViewColumn>
  24.                         <GridViewColumn.CellTemplate>
  25.                             <DataTemplate>
  26.                                 <TextBlock Text="{Binding StudentName}"/>
  27.                             </DataTemplate>
  28.                         </GridViewColumn.CellTemplate>
  29.                         <GridViewColumn.Header>
  30.                             <TextBlock Text="Name"/>
  31.                         </GridViewColumn.Header>
  32.                     </GridViewColumn>
  33.                     <!-- Second Column -->
  34.                     <GridViewColumn>
  35.                         <GridViewColumn.CellTemplate>
  36.                             <DataTemplate>
  37.                                 <TextBlock Text="{Binding Age}"/>
  38.                             </DataTemplate>
  39.                         </GridViewColumn.CellTemplate>
  40.                         <GridViewColumn.Header>
  41.                             <TextBlock Text="Age"/>
  42.                         </GridViewColumn.Header>
  43.                     </GridViewColumn>
  44.                 </GridView>
  45.             </ListView.View>
  46.         </ListView>
  47.         <StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
  48.             <Button x:Name="BtnSort"
  49.                                 Content="Sort Name"
  50.                                 HorizontalAlignment="Left"
  51.                                 VerticalAlignment="Top"
  52.                                 Width="75"/>
  53.             <Button x:Name="BtnSortAge"
  54.                                 Content="Sort Age"
  55.                                 HorizontalAlignment="Left"
  56.                                 VerticalAlignment="Top"
  57.                                 Width="75"/>
  58.         </StackPanel>
  59.     </Grid>
  60. </Window>
  61.  
Конечно, можно было бы "вкладывать" свойства-описания элементов внутрь тегов XAML, сократив немного код. Но приведенный подход демонстрирует "принцип глубокой вложенности" элементов XAML.
Изменим код главного окна:
Код: vb.net
  1. Imports System.ComponentModel
  2.  
  3. Class MainWindow
  4.     Dim CurrentViewOfStudents As ICollectionView
  5.     Public Sub New()
  6.  
  7.         ' This call is required by the designer.
  8.         InitializeComponent()
  9.  
  10.         ' Add any initialization after the InitializeComponent() call.
  11.         CurrentViewOfStudents = _
  12.          CollectionViewSource.GetDefaultView(ListBoxStudentsName.ItemsSource)
  13.     End Sub
  14.  
  15.     Private Sub BtnSort_Click(sender As Object, e As RoutedEventArgs) Handles BtnSort.Click
  16.         ' ========================
  17.         CurrentViewOfStudents.SortDescriptions.Clear()
  18.         CurrentViewOfStudents.SortDescriptions.Add(New SortDescription("StudentName", _
  19.                                                        ListSortDirection.Ascending))
  20.         ' ========================
  21.     End Sub
  22.  
  23.     Private Sub BtnSortAge_Click(sender As Object, e As RoutedEventArgs) Handles BtnSortAge.Click
  24.         ' ========================
  25.         CurrentViewOfStudents.SortDescriptions.Clear()
  26.         CurrentViewOfStudents.SortDescriptions.Add(New SortDescription("Age", _
  27.                                                        ListSortDirection.Ascending))
  28.         ' ========================
  29.     End Sub
  30. End Class
  31.  
И немного "разнообразим" возрасты студентов в классе - поставщике StudentCollectionClass
Код: vb.net
  1. For i As Integer = 0 To 5
  2.             NewFormedCollection.Add(New StudentClass() With _
  3.                                                            {.StudentName = "Boris" & i.ToString,
  4.                                                              .Age = (58 - i).ToString})
  5. Next
  6.  
Получаем следующий результат:


Последовательное нажатие кнопок приводит к сортировке по имени или возрасту.
« Последнее редактирование: Декабрь 27, 2012, 06:24:05 pm от Sergey »

Sergey

  • Administrator
  • Newbie
  • *****
  • Сообщений: 14
    • Просмотр профиля
Data Binding. Start #2 Sort, Filter and grouping
« Ответ #3 : Декабрь 27, 2012, 06:48:59 pm »
Довольная хитрая вещь кроется в разметке
Код: XML
  1. <GridViewColumn.CellTemplate>
  2.                             <DataTemplate>
  3.                                 <TextBlock Text="{Binding Age}"/>
  4.                             </DataTemplate>
  5. </GridViewColumn.CellTemplate>
  6.  
В нашем случае, при отображении лишь простого текста, нет необходимости в использовании шаблона ячейки. Есть, как всегда, "встроенное чудо" под названием DisplayMemberBinding. Формат его таков:
Код: XML
  1. <GridView>
  2.                     <!-- First Column -->
  3.                     <GridViewColumn DisplayMemberBinding="{Binding StudentName}">
  4.                         <GridViewColumn.Header>
  5.                             <TextBlock Text="Name"/>
  6.                         </GridViewColumn.Header>
  7.                     </GridViewColumn>
  8.                     <!-- Second Column -->
  9.                     <GridViewColumn DisplayMemberBinding="{Binding Age}">
  10.                         <GridViewColumn.Header>
  11.                             <TextBlock Text="Age"/>
  12.                         </GridViewColumn.Header>
  13.                     </GridViewColumn>
  14. </GridView>
  15.  
Результат выполнения кода остается прежним.
« Последнее редактирование: Декабрь 27, 2012, 06:52:30 pm от Sergey »

Sergey

  • Administrator
  • Newbie
  • *****
  • Сообщений: 14
    • Просмотр профиля
Data Binding. Start #2 Custom Sort
« Ответ #4 : Декабрь 27, 2012, 08:41:54 pm »
Пользовательская сортировка.

Случаев, когда может понадобиться указанная особенность очень много. Основная идея - автоматизированный (встроенный) механизм сравнения. Итак, добавляем третью колонку в ListView:
Код: XML
  1. <!-- Third Column -->
  2. <GridViewColumn DisplayMemberBinding="{Binding SpammIndexInForums}">
  3.                     <GridViewColumn.Header>
  4.                         <TextBlock Text="Spamm Index"/>
  5.                     </GridViewColumn.Header>
  6. </GridViewColumn>
  7.  
Как видим, отображаемые данные завязаны на SpammIndexInForums - новое свойство у StudentClass. Добавим это свойство и изменим конструктор класса
Код: vb.net
  1. Public Sub New(StudentName As String, _
  2.                    StudentAge As String, _
  3.                    SpammIndex As Integer)
  4.         Me.StudentName = StudentName
  5.         Me.Age = StudentAge
  6.         Me.SpammIndexInForums = SpammIndex
  7. End Sub
  8.  
Функцию, генерирующую коллекцию студентов перепишем в виде
Код: vb.net
  1. Public Sub GetStudentsGroup()
  2.         Dim NewFormedCollection = New ObservableCollection(Of StudentClass)
  3.         NewFormedCollection.Add(New StudentClass() With
  4.                                 {.StudentName = "Nicola", .Age = "39", .SpammIndexInForums = 3})
  5.         NewFormedCollection.Add(New StudentClass() With _
  6.                                 {.StudentName = "Peter", .Age = "43", .SpammIndexInForums = 5})
  7.         NewFormedCollection.Add(New StudentClass() With _
  8.                                 {.StudentName = "Sergey", .Age = "24", .SpammIndexInForums = 2})
  9.         NewFormedCollection.Add(New StudentClass() With _
  10.                                 {.StudentName = "Jan", .Age = "34", .SpammIndexInForums = 0})
  11.         NewFormedCollection.Add(New StudentClass() With _
  12.                                 {.StudentName = "Jeorge", .Age = "18", .SpammIndexInForums = 4})
  13.  
  14.         For i As Integer = 0 To 5
  15.             NewFormedCollection.Add(New StudentClass() With {.StudentName = "Boris" & i.ToString,
  16.                                                              .Age = (58 - i).ToString, _
  17.                                                              .SpammIndexInForums = 6 - i})
  18.         Next
  19.  
  20.         Me.StudentsGroup = NewFormedCollection
  21.     End Sub
  22.  
В StackPanel добавим третью кнопку - пользовательская сортировка, а обработчик события для нее наполним следующим содержанием:
Код: vb.net
  1.  Private Sub BtnSpammSort_Click(sender As Object, e As RoutedEventArgs) Handles BtnSpammSort.Click
  2.         ' ========================
  3.         CurrentViewOfStudents.SortDescriptions.Clear()
  4.         CurrentViewOfStudents.CustomSort = New ComparerSpammer()
  5.         ' ========================
  6.     End Sub
  7.  
Получаем следующий результат сортировки по "индексу спама, посылаемого студентом":

И, наконец, важная вещь, делающая возможной такую сортировку - объявление и приведение коллекции к особому виду. Изменения в коде главного окна:
Код: vb.net
  1. ' Dim CurrentViewOfStudents As ICollectionView
  2.     Dim CurrentViewOfStudents As ListCollectionView
  3.     Public Sub New()
  4.  
  5.         ' This call is required by the designer.
  6.         InitializeComponent()
  7.  
  8.         ' Add any initialization after the InitializeComponent() call.
  9.         CurrentViewOfStudents = CType(
  10.          CollectionViewSource.GetDefaultView(ListBoxStudentsName.ItemsSource), ListCollectionView)
  11.     End Sub
  12.  
Продолжение следует...
« Последнее редактирование: Декабрь 27, 2012, 08:49:03 pm от Sergey »