Подсчет количества видимых элементов в ListBox - PullRequest
2 голосов
/ 06 мая 2009

У меня есть класс под названием Book;

class Book
{
    public string Name { get; set; }
    public string Author { get; set; }
    public int PagesCount { get; set; }
    public int Category { get; set; }
}

В ListBox отображается список книг, а шаблон ItemTemplate был изменен для визуального представления книги. Текст показывает название книги, автора и количество страниц. Категория, однако, представлена ​​определенным цветом (например, история синего цвета, романтика красного цвета и т. Д.). Теперь текст имеет эффект OuterGlowBitmap и преобразователь значений из категории (int) в соответствующий цвет. Все связано в DataTemplate для ListBoxItem. Технически все работает отлично.

Проблема, однако, в производительности. Кажется, что эффект растрового изображения externalGlow сильно влияет на процессор, поэтому, когда у меня есть список из 500 книг, для извлечения данных из базы данных требуется около 500 мс, но для загрузки элементов в ListBox требуется около 10 секунд. И даже когда загрузка завершена, прокрутка очень запаздывает. Я попытался установить для VirtualizingStackPanel.IsVirtualizing значение True, но безрезультатно. (Максимальное количество книг, которые могут находиться в базе данных в любой момент времени, составляет около 30000.)

Однако, даже если в списке больше 100 элементов, человеческий разум не может обработать так быстро, поэтому я не ставлю перед собой цель загрузить и перечислить пользователю все книги, которые искали. Вот почему я создал класс-оболочку навигации BookNavigator, который фактически связывает список со своим объектом ObservableCollection. Все книги загружены в этот BookNavigator, но только X из них отображаются в списке (добавляя их в коллекцию observableCollection).

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

Как рассчитать количество отображаемых элементов, чтобы полоса прокрутки не отображалась?

Две проблемы, которые всплывают: - Изменение размера приложения может изменить размер списка - Не все элементы списка имеют одинаковую высоту (в зависимости от количества авторов).

Есть ли способ достичь того, что я пытаюсь сделать?


РЕДАКТИРОВАТЬ (в ответ на Мартин Харрис)

Проблема с кодом, предложенным Мартином Харрисом, состоит в том, что цикл foreach использует FrameworkElement, но список заполнен объектами типа Book, которые не наследуются от FrameworkElement, и не имеет другого способа вычисления его высоты. Корневым элементом ListBoxItem является сетка, так что, возможно, можно будет восстановить эту сетку, но я не знаю, как это сделать?

Есть ли вообще способ получить фактические элементы пользовательского интерфейса, которые создаются для представления элемента списка?


EDIT

Я нашел этот Q / A, который, кажется, то, что мне нужно .. ItemContainerGenerator

Ответы [ 4 ]

5 голосов
/ 28 сентября 2012

После попытки выяснить что-то подобное, я подумал, что поделюсь своим результатом здесь (как это кажется легче, чем другие ответы):

Простой тест видимости, который я получил от здесь .

private static bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
    if (!element.IsVisible)
        return false;

    Rect bounds =
        element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    var rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}

После этого вы можете просмотреть список элементов списка и использовать этот тест, чтобы определить, какие из них видимы. Счетчик этого списка даст вам количество видимых элементов в списке.

private List<object> GetVisibleItemsFromListbox(ListBox listBox, FrameworkElement parentToTestVisibility)
{
    var items = new List<object>();

    foreach (var item in PhotosListBox.Items)
    {
        if (IsUserVisible((ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item), parentToTestVisibility))
        {
            items.Add(item);
        }
        else if (items.Any())
        {
            break;
        }
    }

    return items;
}

Это будет содержать список элементов, отображаемых в данный момент в списке (в том числе скрытых с помощью прокрутки или чего-то подобного).

2 голосов
/ 06 мая 2009

Сможете ли вы выяснить размеры всех книг, прежде чем добавлять их в список (возможно, путем синтаксического анализа и заполнения шаблона XAML в коде, а затем запрашивать внешний размер для внешнего элемента управления) было бы много работы за небольшую выгоду. Разве вы не можете просто выбрать количество книг, которых будет достаточно для заполнения списка, но не так много, чтобы замедлить рендеринг и выключить полосу прокрутки? Это по-прежнему может быть связано с размером окна списка, чтобы по мере его роста добавлялось больше элементов, но затраты на производительность для добавления нескольких дополнительных элементов должны быть меньше, чем стоимость вычисления всех размеров снова.

Быстрый пример. Допустим, размер книги с одним автором составляет 150 пикселей. Вы можете взять размер списка и разделить его на 125, чтобы получить приблизительную оценку количества элементов, которые будут в поле зрения, но не будут дорогостоящими для расчета. Чего вы хотите избежать, так это слишком мало предметов, так как это оставит пустое место.


Отредактировано с учетом вашего комментария.

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

Этот метод расширения получит последний элемент, который полностью отображается в списке или пустое значение, если элементы не видны:

public static class ListBoxExtensions
{
    public static FrameworkElement GetLastItem(this ListBox listBox)
    {
        double height = listBox.ActualHeight;

        double currentHeight = 0;
        FrameworkElement previous = null;

        foreach (FrameworkElement item in listBox.Items)
        {
            currentHeight += item.ActualHeight;
            if (currentHeight > height)
            {
                return previous;
            }

            previous = item;
        }

        return previous;
    }
}

Если список будет увеличен, вы можете добавить в коллекцию достаточно элементов, чтобы заполнить пробел и повторно запустить процесс.

0 голосов
/ 08 мая 2017

«Как рассчитать количество отображаемых элементов, чтобы полоса прокрутки не отображалась?»

Краткий ответ: (listBox1.Height / listBox1.ItemHeight)

Это дает вам количество отображаемых / доступных строк, так что вы можете прочитать только это количество строк и заполнить весь список.

Теперь демо:

        int listbox_total_visible_lines = 2;
        listBox1.Height = (listbox_total_visible_lines + 1) * listBox1.ItemHeight;
        listBox1.Items.Add("um");
        listBox1.Items.Add("dois");
        listBox1.Items.Add("tres");
        listBox1.Items.Add("quatro");
        listBox1.Items.Add("cinco");
        listBox1.SelectedIndex = listBox1.Items.Count - 1;
        this.Text = (listBox1.Height/ listBox1.ItemHeight).ToString();

Этот пример позволяет вам выбрать количество элементов, которые будут отображаться, поэтому на самом деле доступно количество отображаемых строк. Поэтому вы добавляете только элементы «listbox_total_visible_items», и список будет заполнен и не будет отображать полосы прокрутки.

Код объяснения:

  1. listbox_total_visible_items содержит количество отображаемых строк

  2. список настроек с правильным размером, только 2 строки

3-7. добавить несколько строк

  1. перейти на последнюю строку, просто для удовольствия

  2. показать на текстовой панели формы количество строк списка, основанное на высоте списка, разделенной на размер каждого элемента.

Вот и все.

0 голосов
/ 12 мая 2009

Решение неявно можно найти здесь: Решение

...