Схема потока WPF

У меня есть ListBox с горизонтальной WrapPanel внутри для создания сетки содержимого. Он создает правильный макет, но работает очень медленно с более чем сотней или около того элементов.

Я вижу несколько полусырых попыток виртуализации WrapPanel в Google, но ни одна из них не кажется готовой к производству.

Я пропустил трюк? Как получить производительные и гибкие макеты панелей (требуется перекомпоновка при изменении размера)?

(Примечание: ячейки имеют фиксированный размер).


person NetworkBurger    schedule 11.10.2012    source источник


Ответы (2)


Да, виртуализация — правильный шаг. WPF не может обрабатывать большое количество элементов пользовательского интерфейса одновременно. Написание собственного VirtualizingWrapPanel требует довольно большого опыта работы с WPF. WPF не имеет «готового» решения, поэтому либо приходится его писать, либо использовать чужие наработки.

В отличие от VirtualizingStackPanel, который предлагает ту же функциональность, что и StackPanel, он не полностью ведет себя как WrapPanel по умолчанию, который поставляется с WPF. На это есть несколько причин:

1- WrapPanels могут расти в двух направлениях, тогда как StackPanels растут только вертикально или горизонтально. Кроме того, рост в направлении, ортогональном ориентации панели, зависит от размера элементов в направлении ориентации панели, поэтому невозможно выполнить перенос в новую строку/столбец, если только размер всех элементов в последней строке/ столбец известен, но это невозможно, если виртуализируются два направления панели (поскольку для повышения производительности выполняется непиксельное измерение).

2- Размер экстента такой же, как количество строк/столбцов (разделов). В StackPanel это не проблема: поскольку разрешен только один элемент в разделе, ширина/высота экстента — это количество элементов. В WrapPanel количество разделов сильно зависит от размера элементов, а поскольку размер элементов не фиксирован и может изменяться в любом направлении, невозможно рассчитать количество разделов в WrapPanel.

При разработке этой панели было принято следующее решение:

1- Размеры панели никогда точно не известны. Вместо этого количество разделов оценивается после каждого MeasureOverride с использованием следующего выражения: «количество элементов/среднее число элементов в разделе». Поскольку среднее количество элементов в разделе обновляется после каждой прокрутки, размер панели является динамическим. Экстент будет статическим, если размер элементов в ориентации панелей фиксирован.

2- Раздел элементов/индекс раздела может быть рассчитан только последовательно, поэтому, если вы просматриваете первые элементы и переходите к разделу, который обходит один или несколько нереализованных разделов, панель будет использовать оценку, чтобы узнать, какой элемент отображается первым, это будет исправлено, если вы вернетесь к реализованному разделу и вернетесь последовательно. Например: если панель знает, что элемент 12 находится в разделе 1, и панель оценивает 10 элементов в разделе (раздел 0 является первым), если вы перейдете к разделу 9, панель покажет 100-й элемент как первый видимый элемент ( это будет правильно только в том случае, если от раздела 1 до 9 будет ровно 10 пунктов в разделе). Но если вы вернетесь к разделу 1 и будете обращаться ко всем разделам последовательно, пока не дойдете до раздела 9, тогда панель сохранит все разделы элементов правильно, поэтому дальнейшие оценки не будут выполняться до раздела 10.

3- Обычно WrapPanel внутри средства просмотра прокрутки разрешается прокручивать вертикально и горизонтально, но, поскольку я могу виртуализировать только одно направление, эта панель обтекания будет прокручиваться только в направлении, ортогональном ориентации панелей. (Это означает, что вы не должны устанавливать высоту панели виртуализации/ Ширина явно).

Скопировано из; http://virtualwrappanel.codeplex.com/

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

То, что вы называете «не совсем работает», вероятно, связано с тем, что невозможно сделать хороший VirtualizingWrapPanel, который работал бы точно так же, как один WrapPanel (из-за того, что конкретная строка элементов зависит от следующих элементов и т. д.), но если вы объедините это с тем фактом, что каждый элемент имеет одинаковую ширину/высоту, я думаю, вы могли бы добиться большего.

Кроме того, вы можете начать с написания собственного решения на основе этих статей;

Один: http://blogs.msdn.com/dancre/archive/2006/02/06/implementing-a-virtualized-panel-in-wpf-avalon.aspx

Два: http://blogs.msdn.com/dancre/archive/2006/02/13/531550.aspx

Три: http://blogs.msdn.com/dancre/archive/2006/02/14/532333.aspx

Четыре: http://blogs.msdn.com/dancre/archive/2006/02/16/implementing-a-virtualizingpanel-part-4-the-goods.aspx

person Erti-Chris Eelmaa    schedule 11.10.2012
comment
Я принимаю это как правильный ответ, учитывая, что это единственный вариант. - person NetworkBurger; 19.10.2012

Как насчет того, чтобы попробовать использовать Grid и добавить элемент в Grid, например:

(Но я не проверял его работоспособность)

   public void InitializeGridAsFourColumns()
   {
        for (int i = 0; i < 4; i++)
        {
            _customGrid.ColumnDefinitions.Add(new ColumnDefinition());
        }

   }

    private static int row = -1;
    private static int column = 0;


   public void AddItem(item)
   {

        //Add new RowDefinition every 4 items
        if (column % 4 == 0)
        {
            RowDefinition rd = new RowDefinition();
            rd.Height = GridLength.Auto;
            _customGrid.RowDefinitions.Add(rd);
            row++;
        }

        item.SetValue(Grid.RowProperty, row);
        item.SetValue(Grid.ColumnProperty, column % 4);
        column++;
        _customGrid.Children.Add(item);

   }
person yu yang Jian    schedule 06.03.2017