WPF Textblock性能差

时间:2011-07-24 18:06:01

标签: wpf performance panel textblock

在显示甚至少量数据时,我一直遇到WPF DataGrid和列表框GridView性能问题。我虽然这个问题只是WPF一般表现不佳,但问题似乎只在于文本块控件。

我创建了一个示例面板,我添加了几个项目。如果我添加简单填充的矩形,调整大小/滚动性能是完美的,但是一旦我使用了文本块,性能就会消失。

看起来性能问题来自:

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 个答案:

答案 0 :(得分:5)

TextBox和Rectangle之间的性能差异是由于这些控件的复杂性不同。只需比较结果可视树的复杂性(即使用XamlPad)。矩形很可能只知道它所需的大小。另一方面,TextBox在计算所需大小时需要考虑许多不同的因素,例如所需的实际文本大小(我猜这是真正的瓶颈)。

说到这里,你可能想要尝试一些优化。度量通过的目标是确定您想要的大小。此外,通过在所有子元素上调用measure来传播度量传递。但是,如果您希望更改所需大小,则只需执行此操作。您似乎对包含_rowHeight和_columnWidth字段的布局了解很多。请执行以下操作:

  • 使用:child.Measure(new Size(_columnWidth, _rowHeight))衡量您的孩子。这是实际的约束吗?
  • 减少子元素的度量运行次数。通过将所有这些代码移出MeasureOverride并仅在_rowHeight或_lineWidth更改时调用此函数来执行此操作(此外,这将是在子元素上调用Measure的方法)。将这些字段实现为DependencyProperties以便能够监听更改(如果您不喜欢DependencyProperties,可以使用INotifyPropertyChanged
  • 最有可能的是,您可以在固定时间内实施MeasureOverride(现在无需测量您的子元素)(例如numberOfColumns * _columnWidth...
  • 为ArrangeOverride实现类似的逻辑。
  • 换句话说:不要在MeasureOverride / ArrangeOverride
  • 中做布局逻辑(即决定“这个元素是否适合此行”)

但是,这种方法不符合TextBox元素的所需大小。你可以不关心或单独解决这个问题:

  • 收听文字更改,选择合适的活动。
  • 如果TextBox中的文本发生更改,请仅为此特定文本框调用“测量”。 (您可以使用正无穷大作为约束来测量此文本框)
  • 调整ColumnWidth和RowHeight属性

除了改进MeasureOverride / ArrangeOverride实现之外,您还可以为TextBox使用不同的(例如,更轻量级)ControlTemplate。我会选择重写MeasureOverride / ArrangeOverride。

答案 1 :(得分:3)

首先,在测试代码之后,您似乎已经重写了WPF中已存在的WrapPanel。当我将TestPanel替换为WrapPanel时,行为完全相同,并且性能有所提升。

其次,我想知道什么样的硬件,特别是你正在使用的视频卡。当我在我的电脑上运行这个样本时,我看到很少甚至没有滞后。当然不是你所说的“停顿”。

最后,我知道提高文本呈现性能的唯一方法是使用低级文本对象。我想起了FormattedText。然而,这比使用TextBlock要困难得多,因此我鼓励您在切换到FormattedText之前考虑一下您要完成的工作。

编辑

WPF伤害你的表现的真实区域是layout系统的测量和编排通行证。重要的是要理解每次调整窗口大小时,WPF都会重新计算每个用户界面元素的大小和位置,然后相应地重新排列它们。这对于实现灵活布局或创建动态用户界面非常有用,但对于静态数据网格(这似乎是您的想法),这样做弊大于利。在大多数机器上,WPF会尽可能多地卸载GPU。但是,在您的情况下,CPU正在处理所有内容,因此您在调整大小时会看到“流失”。

FormattedText会更快,但不适合使用数据网格。我不会编写您自己的布局面板(WPF中的1%场景),而是切换到ListView或第三方网格组件,并查看此时的性能。构建(和优化)这些组件以显示大量不断变化的数据 - 构建WPF布局面板以包含其他用户界面元素和图形。