[Каркас приложения. Модель данных. Привязка данных. Табличный вывод.](./wpf_template.md) | [Содержание](../readme.md) | [Поиск, сортировка](./wpf_search_sort.md) # Фильтрация данных В приложениях часто требуется отфильтровать данные либо по словарному полю, либо по каким-либо условиям. ## Фильтрация по словарю Суть фильтрации сводится к тому, что отображается не полный список объектов ("кошек"), а отфильтрованный по словарному полю (тип, категория...). Для получения фильтрованного списка реализуем геттер и сеттер для списка кошек: >Запись типа `public IEnumerable catList { get; set; }` на самом деле является так называемым "синтаксическим сахаром", т.е. сокращённой записью для упрощения написания и повышения читабельности кода. > >При компиляции этот код разворачивается примерно в такой (на самом деле get и set реализуются методами `getcatList` и `setcatList(value)`) > >```cs >private IEnumerable _catList = null; >public IEnumerable catList { > get > { > return _catList; > } > set { > _catList = value; > } >} >``` > >То есть создаётся приватная переменная для хранения реального значения свойства и методы **get** и **set** для, соответственно, получения и сохранения значения свойства. "value" это новое значение свойства, устанавливаемое при присваивании. ```cs private IEnumerable _catList = null; public IEnumerable catList { get { // возвращаем не весь список, а фильтрованный по выбранной породе return _catList .Where(c => selectedBreed == "Все породы" || c.breed.title==selectedBreed); } set { _catList = value; } } ``` Таким обазом, при присваивании полный список "кошек" будет сохраняться в переменной *_catList*, а при чтении будет возвращаться отфильтрованный список При работе с БД у нас обычно есть отдельные модели (таблицы) справочников - реализуем в нашем *поставщике данных* метод, возвращающий справочник пород: 1. Сначала создадим класс для элемента справочника (по идее нам было бы достаточно просто массива строк, но мы сразу будем делать "по-взрослому", чтобы сразу пройтись по всем "граблям", которые встретятся при работе с базой данных) ```cs public class CatBreed { public string title { get; set; } } ``` 1. Создаем в классе главного окна свойство для хранения справочника ```cs public List catBreedList { get; set; } ``` Здесь мы выбрали тип **List**, т.к. нам нужен изменяемый список, в который мы добавим элемент "Все породы" 1. В **интерфейс** поставщика данных (**IDataProvider**) добавляем декларацию метода для получения списка пород ```cs IEnumerable getCatBreeds(); ``` 1. Реализуем этот метод в **LocalDataProvider** Этот метод можно реализовать двумя вариантами: * можно создать список руками ```cs public IEnumerable getCatBreeds() { return new CatBreed[] { new CatBreed{ title="Дворняжка" }, new CatBreed{ title="Шотландская вислоухая" }, new CatBreed{ title="Сиамский" } }; } ``` Из плюсов то, что в списке могут быть породы, которых нет в исходных данных (практически это аналог запроса к базе данных). Минус в том, что вы можете пропустить породу, которая есть в исходных данных. * можно выбрать существующие породы из исходных данных (этот вариант предпочтительнее) Во-первых, немного поменяем класс **LocalDataProvider**, добавив кеширование списка кошек: ```cs public class LocalDataProvider : IDataProvider { // добавляем приватное поле для хранения списка кошек private IEnumerable _cats = null; // в методе выбора кошек добавим проверку списка public IEnumerable getCats() { if (_cats == null) { _cats = new Cat[]{ // тут старый код, формирующий список кошек } } return _cats; } } ``` И реализуем метод, формирующий список пород: ```cs public IEnumerable getCatBreeds() { getCats(); return _cats .Select(c => c.breed) .DistinctBy(b => b.title); } ``` Что тут происходит? * метод _getCats_ заполняет локальный список кошек Допустим исходный массив выглядит так (массив объектов): ```json [ {"breed": {"title": "Порода №1"}, "name": "Имя1"}, {"breed": {"title": "Порода №2"}, "name": "Имя2"}, {"breed": {"title": "Порода №1"}, "name": "Имя3"} ] ``` * метод _Select_ преобразует элемент массива в новый объект, и т.к. мы из всего объекта вернули только одно поле, то на выходе у нас будет список пород `IEnumerable`, содержащий все породы из спика кошек: ```json [ {"title": "Порода №1"}, {"title": "Порода №2"}, {"title": "Порода №1"} ] ``` * метод *DistinctBy* выбирает записи с уникальным значением породы ```json [ {"title": "Порода №1"}, {"title": "Порода №2"} ] ``` 1. Получаем список пород и добавляем в начало "Все породы", чтобы можно было отменить фильтрацию и отображать полный список ```cs // добавляем в класс переменую для хранения текущё выбранной породы private string selectedBreed = "Все породы"; ``` ```cs // в конструкторе получаем список пород catBreedList = Globals.dataProvider.getCatBreeds().ToList(); // и добавляем в начало "все породы" catBreedList.Insert(0, new CatBreed { title = selectedBreed }); ``` 1. Теперь, имея список пород, добавляем в разметку (файл `.xaml`) выпадающий список для выбор породы (во **WrapPanel**): ```xml