Filter в DataGrid - очень медленно!
От: Kolesiki  
Дата: 03.06.21 15:05
Оценка:
Ребят, помогите пожалуйста с гридом — очень медленная фильтрация!
Что делаю (кусочки кода):

grid.ItemsSource = НекийИсточник List<T>

// параметр Func<T, bool> filter
var dataView = CollectionViewSource.GetDefaultView(grid.ItemsSource);
dataView.Filter = (obj) => filter((T)obj);


Далее какой-нть контрол меняет условие фильтрации, после чего я делаю

CollectionViewSource.GetDefaultView(grid.ItemsSource).Refresh()

(что по идее должно те же данные перефильтровать)

Сам фильтр предельно простой: (obj) => obj.Property == true (к примеру). Ну то есть тормозить тут вообще нечему!

Что не нравится: КРАЙНЕ долгая фильтрация даже на 6 элементах!! Кликаю чекбокс, а список обновляется после секунды(!) фильтрации — почему так долго-то?? Подумаешь — Func сконструировал!
Вот не пойму, куда тут вообще копать. Может, грид можно как-то зафорсить "перефильтровать" чем-то помимо Refresh()? (если влиял именно он)
PS
Делал и без всяких хелперов — прям с формы делал GetDefaultView, назначал фильтр — та же медленная петрушка, т.е. дело точно не в Func/лямбдах.
Отредактировано 03.06.2021 17:40 Kolesiki . Предыдущая версия .
Re: Filter в DataGrid - очень медленно!
От: Kolesiki  
Дата: 03.06.21 18:32
Оценка:
Эврика!

Сам нашёл затык, но думаю, многим тоже будет полезно. А дело было... в перекрытом Equals!!

public override bool Equals(object obj)
{
    var b2 = (Beacon)obj;
    if (b2 == null) return false;

    return ID != 0 && ID == b2.ID;
}


Вот эта зараза сразу просадила производительность до целой секунды на смешных 6 элементах — это как вообще?!
Но возникает другой вопрос: каким боком Equals к фильтрации?? Я же сам задаю критерий фильтрования!

К слову: может, что-то не так/медленно/неэффективно в самом Equals? Я его делал для записей СУБД, где очевидно, что если ID равны, то это один и тот же объект и нет смысла сравнивать поля.
Re[2]: Filter в DataGrid - очень медленно!
От: pilgrim_ Россия  
Дата: 03.06.21 18:40
Оценка: +1
Здравствуйте, Kolesiki, Вы писали:

K>
K>public override bool Equals(object obj)
K>{
K>    var b2 = (Beacon)obj;
K>    if (b2 == null) return false;

K>    return ID != 0 && ID == b2.ID;
K>}
K>


..
K>К слову: может, что-то не так/медленно/неэффективно в самом Equals? Я его делал для записей СУБД, где очевидно, что если ID равны, то это один и тот же объект и нет смысла сравнивать поля.

Безотносительно тормозов в фильтрации грида (хз какого ), выделенное условие никогда не сработает (точнее сработает если obj == null), т.к. если obj не приводится к Beacon, то при касте будет исключение. Обычно проверяют через as: obj as Beacon.
Отредактировано 03.06.2021 18:41 pilgrim_ . Предыдущая версия .
Re[2]: Filter в DataGrid - очень медленно!
От: Sharov Россия  
Дата: 04.06.21 00:34
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Но возникает другой вопрос: каким боком Equals к фильтрации?? Я же сам задаю критерий фильтрования!


А как еще-то фильтровать, если не сравнивать содержимое?
Кодом людям нужно помогать!
Re[2]: Filter в DataGrid - очень медленно!
От: samius Япония http://sams-tricks.blogspot.com
Дата: 04.06.21 13:53
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Эврика!


K>Сам нашёл затык, но думаю, многим тоже будет полезно. А дело было... в перекрытом Equals!!


K>
K>public override bool Equals(object obj)
K>{
K>    var b2 = (Beacon)obj;
K>    if (b2 == null) return false;

K>    return ID != 0 && ID == b2.ID;
K>}
K>

По гайдлайнам при перекрытии Equals должен быть переопределен оператор сравнения. Значит, проверка b2 == null пойдет в него.

K>Вот эта зараза сразу просадила производительность до целой секунды на смешных 6 элементах — это как вообще?!

K>Но возникает другой вопрос: каким боком Equals к фильтрации?? Я же сам задаю критерий фильтрования!

K>К слову: может, что-то не так/медленно/неэффективно в самом Equals? Я его делал для записей СУБД, где очевидно, что если ID равны, то это один и тот же объект и нет смысла сравнивать поля.


Чисто версия. Если ID изменяемый, то он может ломать сравнение элементов. От этого может страдать производительность (и не только).
Re: Filter в DataGrid - очень медленно!
От: notacat  
Дата: 14.06.21 17:11
Оценка: +1
вообще, обычно делают наоборот, т.е. сначала заворачивают данные в CollectionViewSource, а потом уже dataView используют как источник в других контролах:

var dataView = CollectionViewSource.GetDefaultView(НекийИсточник List<T>);
grid.ItemsSource = dataView;
dataView.Filter = (obj) => filter((T)obj);

по-идее, если так фильтровать, то Refresh уже не надо вызывать, фильтр автоматически применится. Refresh вызывают, когда надо пересоздать вью по какой-то причине.
Re[3]: Filter в DataGrid - очень медленно!
От: Kolesiki  
Дата: 01.10.21 13:32
Оценка:
Здравствуйте, pilgrim_, Вы писали:

K>>public override bool Equals(object obj)

K>>{
K>> var b2 = (Beacon)obj;
K>> if (b2 == null) return false;


_>... выделенное условие никогда не сработает (точнее сработает если obj == null), т.к. если obj не приводится к Beacon, то при касте будет исключение. Обычно проверяют через as: obj as Beacon.


В моём случае за это можно не беспокоиться — сравнение применяется исключительно для Beacon объектов. Вопрос, за каким якодзуном WPF понадобился этот метод вообще?? Фильтрация грида — там мой кастомный фильтр, никаких Equals не вызывается.
Re[2]: Filter в DataGrid - очень медленно!
От: Kolesiki  
Дата: 01.10.21 13:37
Оценка:
Здравствуйте, notacat, Вы писали:

N>вообще, обычно делают наоборот, т.е. сначала заворачивают данные в CollectionViewSource, а потом уже dataView используют как источник в других контролах:


N>
N>var dataView = CollectionViewSource.GetDefaultView(НекийИсточник List<T>);
N>grid.ItemsSource = dataView;
N>dataView.Filter = (obj) => filter((T)obj);
N>

N>по-идее, если так фильтровать, то Refresh уже не надо вызывать, фильтр автоматически применится. Refresh вызывают, когда надо пересоздать вью по какой-то причине.

Разве при присвоении к ItemsSource не создаётся автоматически DefaultView? (который я потом и получаю)
И фильтр не должен автоматически применяться — ведь грид не знает, от каких контролов зависит фильтр.
Re[3]: Filter в DataGrid - очень медленно!
От: notacat  
Дата: 01.10.21 15:20
Оценка:
N>>
N>>var dataView = CollectionViewSource.GetDefaultView(НекийИсточник List<T>);
N>>grid.ItemsSource = dataView;
N>>dataView.Filter = (obj) => filter((T)obj);
N>>

N>>по-идее, если так фильтровать, то Refresh уже не надо вызывать, фильтр автоматически применится. Refresh вызывают, когда надо пересоздать вью по какой-то причине.

K>Разве при присвоении к ItemsSource не создаётся автоматически DefaultView? (который я потом и получаю)

K>И фильтр не должен автоматически применяться — ведь грид не знает, от каких контролов зависит фильтр.
проблема не в создании DefaultView, а в том, что если ваш грид и другие контролы были бы прибайндены к одному CollectionView, то условия фильтрации жили бы в этом CollectionView, и все контролы бы их просто использовали. Гриду вообще не надо знать, от каких контролов эти условия зависят. Он бы просто ловил изменения условий бесплатно для вас. А так вы руками все туда-сюда копируете, да еще рефрешите, конечно долго. Что на самом деле делает DataGrid с вашим источником данных, если он не CollectionView, слишком глубоко закопано. В принципе, гриды обычно в таких случаях ваш источник заворачивают во что-то, с чем удобно работать. Дополнительные накладные расходы, скорей всего еще один CollectionView
В смысле производительности гораздо дешевле создать нормальный источник, и забайндить его везде, чтобы грид с ним напрямую работал
Re[4]: Filter в DataGrid - очень медленно!
От: Kolesiki  
Дата: 03.10.21 11:51
Оценка:
Здравствуйте, notacat, Вы писали:

N>>>
N>>>var dataView = CollectionViewSource.GetDefaultView(НекийИсточник List<T>);
N>>>grid.ItemsSource = dataView;
N>>>dataView.Filter = (obj) => filter((T)obj);
N>>>

N>>>по-идее, если так фильтровать, то Refresh уже не надо вызывать, фильтр автоматически применится. Refresh вызывают, когда надо пересоздать вью по какой-то причине.

K>>Разве при присвоении к ItemsSource не создаётся автоматически DefaultView? (который я потом и получаю)

K>>И фильтр не должен автоматически применяться — ведь грид не знает, от каких контролов зависит фильтр.

N>проблема не в создании DefaultView, а в том, что если ваш грид и другие контролы были бы прибайндены к одному CollectionView....


Шта?? К коллекции прибайнден только грид.

N>, то условия фильтрации жили бы в этом CollectionView


Они и так там живут! Я же фильтр указал — он всегда и вызывается, ничего не пересоздаётся.

N>Гриду вообще не надо знать, от каких контролов эти условия зависят. Он бы просто ловил изменения условий бесплатно для вас


КАК? У меня текстовое поле, комбобокс, чекбоксы, радиобатоны... как он их будет отслеживать? Я делаю ровно наоборот — при изменении в фильтрующих контролах даю гриду команду "обновись".

N> А так вы руками все туда-сюда копируете, да еще рефрешите, конечно долго


Что именно копирую?? Рефрешу — да, но грид по-любому должен рефрешиться, как иначе он отфильтрует данные??

N> Что на самом деле делает DataGrid с вашим источником данных, если он не CollectionView, слишком глубоко закопано. В принципе, гриды обычно в таких случаях ваш источник заворачивают во что-то, с чем удобно работать.


Ну да. в DefaultView! Какой смысл мне его создавать, если грид и так его сделает??

N> Дополнительные накладные расходы, скорей всего еще один CollectionView


Нет никаких дополнительных — всё тот же вью, который грид создал под капотом.

N>В смысле производительности гораздо дешевле создать нормальный источник, и забайндить его везде, чтобы грид с ним напрямую работал


Список и есть "нормальный источник", мне не нужно лезть в потроха грида (или другого списочного элемента). Собственно, нам для этого и выставили ItemsSource, чтобы мы туда напрямую присваивали наши коллекции, не заботясь о деталях реализации.
Re[5]: Filter в DataGrid - очень медленно!
От: notacat  
Дата: 03.10.21 12:29
Оценка:
.. не могу настаивать против такой убежденности, может у вас данных мало, чтобы заметить проблемы с вашим подходом.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.