瀏覽代碼

причесал фильтр

Евгений Колесников 11 月之前
父節點
當前提交
abfb788531
共有 3 個文件被更改,包括 108 次插入58 次删除
  1. 107 57
      articles/wpf_filtering.md
  2. 1 1
      articles/wpf_template.md
  3. 二進制
      img/wpf_03.png

+ 107 - 57
articles/wpf_filtering.md

@@ -26,15 +26,13 @@
 >То есть создаётся приватная переменная для хранения реального значения свойства и методы **get** и **set** для, соответственно, получения и сохранения значения свойства. "value" это новое значение свойства, устанавливаемое при присваивании.
 
 ```cs
-public string selectedBreed = "";
-
 private IEnumerable<Cat> _catList = null;
 public IEnumerable<Cat> catList {
     get
     {
         // возвращаем не весь список, а фильтрованный по выбранной породе
         return _catList
-            .Where(c=>(selectedBreed=="Все породы" || c.breed==selectedBreed));
+            .Where(c => selectedBreed == "Все породы" || c.breed.title==selectedBreed);
     }
     set {
         _catList = value;
@@ -89,72 +87,88 @@ public IEnumerable<Cat> catList {
 
     * можно выбрать существующие породы из исходных данных (этот вариант предпочтительнее)
 
-        >Первоначальный вариант, как оказалось, не работает в **WPF** (видимо метод *DiastinctBy*) появился в C# более старших версий
-        >
-        >```cs
-        >public IEnumerable<CatBreed> getCatBreeds()
-        >{
-        >    return _catList.DistinctBy(cat => cat.breed)
-        >        .Select(cat => new CatBreed { title = cat.breed });
-        >}
-        >```
-        >
-        >Что тут происходит?
-        >
-        >* метод *DistinctBy* выбирает записи с уникальным значением породы
-        >* метод *Select* преобразует исходный список - вместо списка кошек получаем список пород
+        Во-первых, немного поменяем класс **LocalDataProvider**, добавив кеширование списка кошек:
+
+        ```cs
+        public class LocalDataProvider : IDataProvider
+        {
+            // добавляем приватное поле для хранения списка кошек
+            private IEnumerable<Cat> _cats = null;
+
+            // в методе выбора кошек добавим проверку списка
+            public IEnumerable<Cat> getCats()
+            {
+                if (_cats == null)
+                {
+                    _cats = new Cat[]{
+                        // тут старый код, формирующий список кошек
+                    }
+                }
+                return _cats;
+            }
+        }
+        ```
+
+        И реализуем метод, формирующий список пород:
 
         ```cs
         public IEnumerable<CatBreed> getCatBreeds()
         {
-            return _catList
-                .Select(cat => cat.breed)
-                .Distinct()
-                .Select(breed => new CatBreed { title = breed });
+            getCats();
+            return _cats
+                .Select(c => c.breed)
+                .DistinctBy(b => b.title);
         }
         ```
 
         Что тут происходит?
 
-        Допустим исходный массив выглядит так (массив объектов): 
-
-        ```
-        [
-            {breed: "Порода №1", name: "Имя1"}, 
-            {breed: "Порода №2", name: "Имя2"}, 
-            {breed: "Порода №1", name: "Имя3"} 
-        ]
-        ```
+        * метод _getCats_ заполняет локальный список кошек
 
-        * метод *Select* преобразует элемент массива в новый объект, и т.к. мы из всего объекта вернули только одно поле, то на выходе у нас будет массив пород:
+            Допустим исходный массив выглядит так (массив объектов): 
 
-            ```
+            ```json
             [
-                "Порода №1", 
-                "Порода №2",
-                "Порода №1" 
+                {"breed": {"title": "Порода №1"}, "name": "Имя1"}, 
+                {"breed": {"title": "Порода №2"}, "name": "Имя2"}, 
+                {"breed": {"title": "Порода №1"}, "name": "Имя3"} 
             ]
             ```
 
-        * метод *Distinct* вернёт массив уникальных занчений:
+        * метод _Select_ преобразует элемент массива в новый объект, и т.к. мы из всего объекта вернули только одно поле, то на выходе у нас будет список пород `IEnumerable<CatBreed>`, содержащий все породы из спика кошек:
 
-            ```
+            ```json
             [
-                "Порода №1", 
-                "Порода №2"
+                {"title": "Порода №1"},
+                {"title": "Порода №2"},
+                {"title": "Порода №1"}
             ]
             ```
 
-        * второй вызов *Select* преобразует массив строк в массив объектов типа **CatBreed**
+        * метод *DistinctBy* выбирает записи с уникальным значением породы
+
+            ```json
+            [
+                {"title": "Порода №1"},
+                {"title": "Порода №2"}
+            ]
+            ```
 
 1. Получаем список пород и добавляем в начало "Все породы", чтобы можно было отменить фильтрацию и отображать полный список
 
     ```cs
+    // добавляем в класс переменую для хранения текущё выбранной породы
+    private string selectedBreed = "Все породы";
+    ```
+
+    ```cs
+    // в конструкторе получаем список пород
     catBreedList = Globals.dataProvider.getCatBreeds().ToList();
-    catBreedList.Insert(0, new CatBreed { title = "Все породы" });
+    // и добавляем в начало "все породы"
+    catBreedList.Insert(0, new CatBreed { title = selectedBreed });
     ```
 
-1. Теперь, имея список пород, добавляем в разметку (файл .xaml) выпадающий список для выбор породы (во WrapPanel):
+1. Теперь, имея список пород, добавляем в разметку (файл `.xaml`) выпадающий список для выбор породы (во **WrapPanel**):
 
     ```xml
     <Label 
@@ -163,24 +177,60 @@ public IEnumerable<Cat> catList {
 
     <ComboBox
         Name="BreedFilterComboBox"
-        SelectionChanged="BreedFilterComboBox_SelectionChanged"
         VerticalAlignment="Center"
-        MinWidth="100"
+        MinWidth="150"
         SelectedIndex="0"
-        ItemsSource="{Binding catBreedList}">
-
-        <ComboBox.ItemTemplate>
-            <DataTemplate>
-                <Label 
-                    Content="{Binding title}"/>
-            </DataTemplate>
-        </ComboBox.ItemTemplate>
-    </ComboBox>
+        ItemsSource="{Binding catBreedList}"
+    />
     ```
 
-    Элемент **ComboBox** предназначен для отображения списка *строк*. Для того, чтобы отобразить элементы произвольного списка используется шаблон **ComboBox.ItemTemplate**, в котором можно реализовать произвольный вид элемента списка (вставить картинки, раскрасить и т.п.) и, в нашем случае, в качестве содержимого выбрать свойство объекта для отображения. 
+    Если запустить программу в таком виде, то увидим примерно такое:
 
-    >Можно сделать проще, используя переопределение метода *getString*, но пока оставим так.
+    ![](../img/wpf_03.png)
+
+    Вместо названий пород у нас имя класса, почему так происходит?
+
+    Элемент **ComboBox** предназначен для отображения списка **строк**, т.е. где-то под капотом он для элемента списка вызывает метод _ToString_, а этот матод как раз и возвращает название класса.
+    
+    Для того, чтобы отобразить элементы другого типа данных можно использовать два варината:
+    
+    * Использовать шаблон **ComboBox.ItemTemplate**, в котором можно реализовать произвольный вид элемента списка (вставить картинки, раскрасить и т.п.) и, в нашем случае, в качестве содержимого выбрать строковое свойство объекта для отображения. 
+
+        ```cs
+        <ComboBox
+            Name="BreedFilterComboBox"
+            VerticalAlignment="Center"
+            MinWidth="150"
+            SelectedIndex="0"
+            ItemsSource="{Binding catBreedList}"
+        >
+            <ComboBox.ItemTemplate>
+                <DataTemplate>
+                    <Label 
+                        Content="{Binding title}"/>
+                </DataTemplate>
+            </ComboBox.ItemTemplate>
+        </ComboBox>
+        ```
+
+    * Первый вариант, на мой взгляд, слишком сложен, если нам нужно вывести только одно строковое поле. В таком случае проще переопределить метод _ToString_ в классе **CatBreed**:
+
+        ```cs
+        public class CatBreed
+        {
+            public string title { get; set; }
+            public override string ToString() => title;
+        }
+        ```
+
+1. Добавим в **ComboBox** обработчик события выбора: **SelectionChanged**
+
+    ```xml
+    <ComboBox
+        Name="BreedFilterComboBox"
+        SelectionChanged="BreedFilterComboBox_SelectionChanged"
+        ...
+    ````
 
 1. В классе главного окна в обработчике события выбора породы (*BreedFilterComboBox_SelectionChanged*) запоминаем выбранную породу
 
@@ -253,7 +303,7 @@ public IEnumerable<Cat> catList {
     };
     ```
 
-1. В разметке меняем привязку   
+1. В разметке меняем привязку (аттрибут _ItemsSource_)
 
     ```xml
     <ComboBox
@@ -289,7 +339,7 @@ public IEnumerable<Cat> catList {
     get
     {
         return _catList
-            .Where(c=>(c.age>=selectedAge.ageFrom && c.age<selectedAge.ageTo));
+            .Where(c=>(c.age >= selectedAge.ageFrom && c.age<selectedAge.ageTo));
     }
     ```
 

+ 1 - 1
articles/wpf_template.md

@@ -181,7 +181,7 @@
 Теперь, имея данные для отображения, мы можем разместить в разметке элемент **DataGrid** и "привязать" к нему данные:
 
 ```xml
- <DataGrid
+<DataGrid
     Grid.Row="1"
     Grid.Column="1"
     CanUserAddRows="False"

二進制
img/wpf_03.png