Виртуализация DataGrid при обновлении представления коллекции - PullRequest
0 голосов
/ 20 мая 2019

В частности, это продолжение этого вопроса Производительность фильтра DataGrid , но есть еще много похожих вопросов, касающихся производительности WPF DataGrid в StackOverflow.

После большого количества профилирования и изучения исходного кода .NET я понял, что многие проблемы с производительностью, такие как фильтрация и сортировка , сводятся к одной проблеме: A CollectionView Событие .Reset не перезапускает контейнеры (как прокрутка).

Я имею в виду, что вместо назначения существующим строкам нового текста данных все строки удаляются из визуального дерева, создаются и добавляются новые строки, и выполняется цикл компоновки (измерение и аранжировка).

Итак, главный вопрос: Кому-нибудь удалось успешно обойти это? Например. путем ручного манипулирования ItemContainerGenerator или создания собственной версии DataGridRowsPresenter?

Так что это суть моего подхода до сих пор.

public class CollectionViewEx
{
    public event EventHandler Refresh;

    public override void Refresh()
    {
        Refresh?.Invoke(this, EventArgs.Empty);
    }
}

public class DataGridEx : DataGrid
{
    protected override OnItemsSourceChanged(IEnumerable oldSource, IEnumerable newSource)
    {
        if (newSource is CollectionViewEx cvx)
        {
            cvx.Refresh += (o,a) => OnViewRefreshing;
        }
    }

    private void OnViewRefreshing()
    {
        RowsPresenter.Refresh();
    }
}

public class DataGridRowsPresenterEx : DataGridRowsPresenter
{
    public void Refresh()
    {
        var generator = (IRecyclingItemContainerGenerator)ItemContainerGenerator;

        generator.Recycle(new GeneratorPosition(0, 0), ???);         
        RemoveInternalChildRange(0, VisualChildrenCount);

        using (generator.StartAt(new GeneratorPosition(-1, 0), GeneratorDirection.Forward))
        {
            UIElement child;
            bool isNewlyRealised = false;
            while ((child = generator.GenerateNext(out isNewlyRealised) as UIElement) != null)
            {
                AddInternalChild(child);
                generator.PrepareItemContainer(child);
            }
        }
    }
}

Но результаты очень запутанные - очевидно, потому что я не совсем понимаю, как работать с ICG.

Я просмотрел исходный код .net, чтобы увидеть их реализации (при добавлении / удалении / замене элементов), а также нашел несколько онлайн-ресурсов о том, как создать новую виртуализированную панель (например, virtualizingwrappanel), но ни один из них не касается эта конкретная проблема, где мы хотим повторно использовать все существующие контейнеры для нового набора элементов .

Итак, второстепенный вопрос: Может кто-нибудь объяснить, возможен ли такой подход? Как бы я это сделал?

...