Плохая производительность текстового блока WPF - PullRequest
4 голосов
/ 24 июля 2011

У меня были проблемы с производительностью WPF DataGrid и listbox GridView при отображении даже небольших объемов данных. Я думал, что эта проблема была просто в WPF с низкой производительностью в целом, но проблема, похоже, заключается только в элементе управления textblock.

Я создал образец панели, к которой добавил несколько элементов. Если я добавлю прямоугольники, которые просто заполнены, производительность изменения размера / прокрутки будет идеальной, но как только я использую текстовые блоки, производительность выходит за рамки.

Похоже, что проблема производительности возникает из:

child.Measure(constraint); 

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

РЕДАКТИРОВАТЬ: Теперь я создал упрощенный код для упорядочивания элементов, как я хотел.

Производительность этого кода велика, за исключением ... когда ширина текста внутри текстового блока превышает фактическую ширину текстового блока. Это снова приводит к снижению производительности - возможно, потому, что он пытается снова измерить элементы?

 public class TestPanel : Panel
{
    private int _rowHeight = 20;
    private int _columnWidth = 50;

    public TestPanel()
    {

        for (int i = 0; i < 100; i++)
        {

            for (int j = 0; j < 20; j++)
            {
                TextBlock cell = new TextBlock();
                cell.ClipToBounds = true;
                cell.Width = _columnWidth;
                cell.Height = _rowHeight;
                cell.Text = i.ToString() + ":" + j.ToString();
                this.Children.Add(cell);
            }
        }
    }

    protected override Size MeasureOverride(Size constraint)
    {
        return new Size(_columnWidth*20,_rowHeight*100);
    }

    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        UIElementCollection children = InternalChildren;

        for (int i = 0; i < 100; i++)
        {
            for (int j = 0; j < 20; j++)
            {
                UIElement child = children[i*20+j];
                child.Arrange(new Rect(j * _columnWidth, i * 20, _columnWidth, 20));
            }
        }
        return arrangeBounds;
    }
}

public MainWindow()
    {
        InitializeComponent();

        TestPanel myPanel = new TestPanel();
        ScrollViewer scroll = new ScrollViewer();

        myPanel.Background = Brushes.Aqua;

        scroll.Content = myPanel;
        this.Content = scroll;

    }

Ответы [ 2 ]

5 голосов
/ 25 июля 2011

Разница в производительности между TextBox и Rectangle связана с различной сложностью этих элементов управления.Просто сравните сложность построения визуальных деревьев (т.е. используя XamlPad).Прямоугольник, скорее всего, просто знает желаемый размер.TextBox, с другой стороны, должен учитывать множество различных факторов при расчете желаемого размера, таких как желаемый размер острого текста (я полагаю, это реальное узкое место).

С учетом сказанного,некоторые оптимизации, которые вы можете попробовать.Цель прохода измерения - определить желаемый размер.Кроме того, вы передаете проход меры путем вызова меры для всех дочерних элементов.Однако вам нужно сделать это только в том случае, если вы ожидаете изменения желаемого размера.Похоже, вы много знаете о макете с полями _rowHeight и _columnWidth.Сделайте следующее:

  • Измерьте своих детей, используя: child.Measure(new Size(_columnWidth, _rowHeight)).Это фактическое ограничение, верно?
  • Уменьшите количество прогонов мер для ваших дочерних элементов.Сделайте это, переместив весь этот код из MeasureOverride и вызывайте эту функцию только в случае изменения _rowHeight или _lineWidth (также это будет метод, который вызывает Measure для ваших дочерних элементов).Реализуйте эти поля как DependencyProperties , чтобы иметь возможность прослушивать изменения (вы можете использовать INotifyPropertyChanged , если вам не нравится DependencyProperties)
  • Скорее всего, вы можете реализовать MeasureOverride(теперь без необходимости измерять ваши дочерние элементы) в постоянное время (например, numberOfColumns * _columnWidth...)
  • Реализация аналогичной логики для ArrangeOverride.
  • Другими словами: не выполняйте логику макета (т. Е. Решайте вопросы типа «вписывается ли этот элемент в эту строку») в MeasureOverride / ArrangeOverride

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

  • Прослушивание изменений текста, выберите соответствующее событие.
  • Если текст в TextBox изменится, вызовите Measure только для этого конкретного текстакоробка.(Вы можете измерить это текстовое поле с положительной бесконечностью в качестве ограничения)
  • Адаптируйте ваши свойства ColumnWidth и RowHeight

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

3 голосов
/ 25 июля 2011

Прежде всего, после тестирования вашего кода кажется, что вы переписали WrapPanel, который уже существует в WPF.Когда я заменил ваш TestPanel на WrapPanel, поведение было точно таким же, с улучшением производительности.

Во-вторых, мне интересно, какое оборудование, в частности, видеокарту вы используете.Когда я запустил этот образец на своем компьютере, я увидел, что почти нет лагов.Конечно, это не та «остановка заточки», о которой вы говорите.

Наконец, единственный известный мне способ улучшить производительность рендеринга текста - это использовать низкоуровневые текстовые объекты.FormattedText приходит на ум.Однако работать с этим гораздо сложнее, чем с TextBlock, поэтому я рекомендую вам подумать о том, чего вы пытаетесь достичь, прежде чем переходить на FormattedText.

РЕДАКТИРОВАТЬ :

Реальная область, в которой WPF наносит ущерб вашей производительности, - это проходы Измерения и организации системы layout .Важно понимать, что каждый раз, когда вы изменяете размер окна, WPF пересчитывает размеры и позиции каждого элемента пользовательского интерфейса, а затем перестраивает их соответствующим образом.Это чрезвычайно полезно для достижения гибких макетов или создания динамических пользовательских интерфейсов, но для статической сетки данных (которая, как вам кажется, имеет в виду), это приносит больше вреда, чем пользы.На большинстве машин WPF будет перекладывать как можно больше работы на графический процессор.В вашем случае, однако, процессор обрабатывает все, поэтому "отток", который вы видите при изменении размера.

FormattedText будет быстрее, но не будет пригоден для работы с сеткой данных.Вместо того, чтобы писать собственную панель макета (сценарий 1% в WPF), я бы переключился на ListView или сторонний компонент сетки и посмотрел, как производительность в этот момент.Компоненты такого типа создаются (и оптимизируются) для отображения огромных рядов изменяющихся данных - панели макета WPF созданы так, чтобы содержать другие элементы пользовательского интерфейса и чертежи.

...