Очень часто, в приложениях приходится фильтровать отображаемые списки. Чуть реже, но тоже достаточно часто их приходится группировать. Каждый раз изобретаются велосипеды. Сегодня я хочу показать стандартный способ фильтрации и группировки эементов в коллекциях. Само собой, пример будет на WPF.
Для демонстрации воспользуемся вот таким классом:
public string Name { get; set; }
get
{
if (_demo == null)
{
_demo = new List<Item>();
_demo.Add(new Item() { Name = "Плоский мир", Type = "Настольная игра" });
_demo.Add(new Item() { Name = "Коньки", Type = "Спортивные товары" });
_demo.Add(new Item() { Name = "Воланчик", Type = "Спортивные товары" });
_demo.Add(new Item() { Name = "Геополитика", Type = "Настольная игра" });
_demo.Add(new Item() { Name = "Футбольный мяч", Type = "Спортивные товары" });
_demo.Add(new Item() { Name = "Каркасон", Type = "Настольная игра" });
_demo.Add(new Item() { Name = "Город синей луны", Type = "Настольная игра" });
}
return _demo;
}
}
}
Приведенный класс содержит два свойства, одно для показа, второе для группировки, ну и статическое свойство для генерации демонстрационной коллекции.
Для того, чтобы получить все возможности фильтрации, группировки (есть еще сортировка, но т.к. в DataGrid она может определяться пользователем, то здесь ее рассматривать не буду), нам придется воспользоваться классом CollectionViewSource. Данный класс позволяет через свойство GroupDescription задавать группировки, а через свойство Filter - предикат, который будет определеять, показывать элемент коллекции или нет.
Делаем разметку формы:
<RowDefinition Height="auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBox x:Name="tbFilter" Margin="5" TextChanged="tbFilter_TextChanged" />
<ListView x:Name="lvMain" Grid.Row="1" Margin="5">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Foreground="Blue" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Обратите внимание, что способ отображения группировки задается при поможи DataTemplate, в котором мы можем определить любое отображение для группировки. Ну и настройка CollectionViewSource может выглядеть вот так:
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
ICollectionView view = CollectionViewSource.GetDefaultView(Item.Demo);
view.Filter = str => (str as Item).Name.ToLower().Contains(tbFilter.Text.ToLower());
view.GroupDescriptions.Add(new PropertyGroupDescription("Type"));
lvMain.ItemsSource = view;
}
if (lvMain.ItemsSource is ICollectionView)
{
(lvMain.ItemsSource as ICollectionView).Refresh();
}
}
}
Обратите внимание, на то, что группировку мы ведем указав имя свойства элемента коллекции, а всю остальную работу за нас сделает CollectionViewSource. Для фильтрации же, достаточно задать предикат, который на вход получает элемент коллекции, и возвращает true, если элемент коллекции необходимо отображать, иначе false. Последний метод необходим, чтобы после имзенения текста в TextBox происходилаперепроверка всех элементов методом помещенным в Filter. Ну и вот так это работает:
На этом все.
Для демонстрации воспользуемся вот таким классом:
public
class Item
{public string Name { get; set; }
public string Type { get; set; }
static private List<Item> _demo;
public static List<Item> Demo
{get
{
if (_demo == null)
{
_demo = new List<Item>();
_demo.Add(new Item() { Name = "Плоский мир", Type = "Настольная игра" });
_demo.Add(new Item() { Name = "Коньки", Type = "Спортивные товары" });
_demo.Add(new Item() { Name = "Воланчик", Type = "Спортивные товары" });
_demo.Add(new Item() { Name = "Геополитика", Type = "Настольная игра" });
_demo.Add(new Item() { Name = "Футбольный мяч", Type = "Спортивные товары" });
_demo.Add(new Item() { Name = "Каркасон", Type = "Настольная игра" });
_demo.Add(new Item() { Name = "Город синей луны", Type = "Настольная игра" });
}
return _demo;
}
}
}
Приведенный класс содержит два свойства, одно для показа, второе для группировки, ну и статическое свойство для генерации демонстрационной коллекции.
Для того, чтобы получить все возможности фильтрации, группировки (есть еще сортировка, но т.к. в DataGrid она может определяться пользователем, то здесь ее рассматривать не буду), нам придется воспользоваться классом CollectionViewSource. Данный класс позволяет через свойство GroupDescription задавать группировки, а через свойство Filter - предикат, который будет определеять, показывать элемент коллекции или нет.
Делаем разметку формы:
<Grid>
<Grid.RowDefinitions><RowDefinition Height="auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBox x:Name="tbFilter" Margin="5" TextChanged="tbFilter_TextChanged" />
<ListView x:Name="lvMain" Grid.Row="1" Margin="5">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Foreground="Blue" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Обратите внимание, что способ отображения группировки задается при поможи DataTemplate, в котором мы можем определить любое отображение для группировки. Ну и настройка CollectionViewSource может выглядеть вот так:
public
partial class MainWindow : Window
{public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void
MainWindow_Loaded(object sender, RoutedEventArgs e)
{ICollectionView view = CollectionViewSource.GetDefaultView(Item.Demo);
view.Filter = str => (str as Item).Name.ToLower().Contains(tbFilter.Text.ToLower());
view.GroupDescriptions.Add(new PropertyGroupDescription("Type"));
lvMain.ItemsSource = view;
}
private void tbFilter_TextChanged(object
sender, TextChangedEventArgs e)
{if (lvMain.ItemsSource is ICollectionView)
{
(lvMain.ItemsSource as ICollectionView).Refresh();
}
}
}
Обратите внимание, на то, что группировку мы ведем указав имя свойства элемента коллекции, а всю остальную работу за нас сделает CollectionViewSource. Для фильтрации же, достаточно задать предикат, который на вход получает элемент коллекции, и возвращает true, если элемент коллекции необходимо отображать, иначе false. Последний метод необходим, чтобы после имзенения текста в TextBox происходилаперепроверка всех элементов методом помещенным в Filter. Ну и вот так это работает:
На этом все.
Комментариев нет:
Отправить комментарий