WPF 文本块性能较差

发布于 2024-11-26 00:01:18 字数 1863 浏览 2 评论 0原文

当显示少量数据时,我一直遇到 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;

    }

I have been having trouble with the WPF DataGrid and listbox GridView performance when displaying even small amounts of data. I though this problem was simply WPF having poor performance in general, but the problem seems to lie in the textblock control only.

I created a sample panel that I added several items to. If I add rectangles that are simply filled, the resizeing/scroll performance is perfect, but once I use textblocks, the performance goes out the window.

It looks like the performance issue arises from:

child.Measure(constraint); 

When the textblock gets measured, it brings performance to a grinding halt. Is there anything that I can to to override the measurement of a textblock or something to improve performance? (I will set the size of the children explicitly)

EDIT: I have now created simplified code to arrange the items as I wanted.

The performance of this code is great except...when the width of the text inside the textblock exceed the actual width of the textblock. This brings my performance back down to a crawl - possibly because it is trying to measure the elements again?

 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;

    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眼波传意 2024-12-03 00:01:18

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

话虽如此,您可能想尝试一些优化。测量过程的目的是确定您所需的尺寸。此外,您还可以通过在所有子元素上调用measure来传播measure pass。但是,仅当您期望更改所需大小时才需要执行此操作。看来您对具有 _rowHeight 和 _columnWidth 字段的布局了解很多。因此,请执行以下操作:

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

但是,此方法不考虑 TextBox 元素的所需大小。您可以不关心或单独解决这个问题:

  • 监听文本更改,选择适当的事件。
  • 如果文本框中的文本发生更改,则仅针对该特定文本框调用 Measure。 (您可以使用正无穷大作为约束来测量此文本框)
  • 调整 ColumnWidth 和 RowHeight 属性

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

The performance difference between TextBox and Rectangle is due to the different complexity of these controls. Just compare the complexity of the resulitng visual trees (i.e. using XamlPad). A Rectangle most likely just knows its desired size. A TextBox, on the other hand, needs to consider many different factors when calculating the desired size, such as the desired size of the acutal text (I guess this is the real bottleneck).

Having that said, there are some optimizations you might want to try. The goal of the measure pass is to determine your desired size. Furthermore, you propagate the measure pass by calling measure on all child elements. However, you only need to do this if you expect a change of the desired size. It seems like you know a lot about your layout having _rowHeight and _columnWidth fields. So do the following:

  • Measure your children using: child.Measure(new Size(_columnWidth, _rowHeight)). This is the actual constraint right?
  • Reduce the number of measure runs for your child elements. Do this by moving all this code out of MeasureOverride and only call this function if _rowHeight or _lineWidth changes (also, this will be the method which calls Measure on your child elements). Implement these fields as DependencyProperties to be able to listen to changes (you can use INotifyPropertyChanged if you don't like DependencyProperties)
  • Most likely, you can implement MeasureOverride (now without having to measure your child elements) in constant time (e.g. numberOfColumns * _columnWidth...)
  • Implement similar logic for ArrangeOverride.
  • In other words: don't do layout logic (i.e. deciding questions like "does this element fit into this line") in MeasureOverride/ArrangeOverride

This approach, however, does not respect the desired size of the TextBox elements. You can either not care or solve this separately:

  • Listen to text changes, choose the appropriate event.
  • If the text in a TextBox changes, call Measure only for this particular text box. (You can measure this textbox with positive infinity as constraint)
  • Adapt your ColumnWidth and RowHeight properties

Apart from improving your MeasureOverride/ArrangeOverride implementations you can use a different (e.g. more lightweight) ControlTemplate for the TextBox. I would opt for rewriting MeasureOverride/ArrangeOverride.

韵柒 2024-12-03 00:01:18

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

其次,我想知道您使用的是哪种硬件,特别是显卡。当我在我的电脑上运行这个示例时,我几乎没有看到任何延迟。当然不是你说的“磨蹭”。

最后,据我所知,提高文本渲染性能的唯一方法是使用低级文本对象。我想到了 FormattedText 。然而,这比 TextBlock 更难使用,因此我鼓励您在切换到 FormattedText 之前考虑一下您想要完成的任务是什么。

编辑

WPF 损害性能的真正区域是 布局系统。重要的是要了解,每次调整窗口大小时,WPF 都会重新计算每个用户界面元素的大小和位置,然后相应地重新排列它们。这对于实现灵活的布局或创建动态用户界面非常有用,但对于静态数据网格(这似乎正是您的想法),这弊大于利。在大多数计算机上,WPF 会将尽可能多的工作卸载给 GPU。然而,就您而言,CPU 正在处理所有事情,因此您在调整大小时会看到“搅动”。

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

First of all, after testing your code it appears that you've rewritten the WrapPanel that already exists in WPF. When I replaced your TestPanel with the WrapPanel the behavior was exactly the same, with an improvement to performance.

Secondly, I am wondering what kind of hardware, specifically, video card you are using. When I ran this sample on my PC I saw little to no lag. Certainly not the "grinding halt" that you are speaking of.

Finally, the only way I know of to improve the performance of text-rendering is to use low-level text objects. FormattedText comes to mind. This is far more difficult to work with than TextBlock, however, so I would encourage you to think about what it is you are trying to accomplish before switching to FormattedText.

EDIT:

The real area where WPF is hurting your performance is in the Measure and Arrange passes of the layout system. It's important to understand that every time you resize the window, WPF is re-calculating the sizes and positions of every user-interface element, and then rearranging them accordingly. This is extremely useful for achieving flexible layouts or creating dynamic user interfaces, but for a static grid of data (which seems to be what you have in mind), this is doing more harm than good. On most machines, WPF will offload as much work as possible to the GPU. In your case, however, the CPU is handling everything, hence the "churn" you are seeing on resize.

FormattedText would be faster, but would not lend itself to working with a data-grid. Rather than writing your own layout panel (a 1% scenario in WPF), I would switch to a ListView or a third-party grid component and see how performance is at that point. These kind of components are built (and optimized) to display vast rows of changing data-- the WPF layout panels are built to contain other user interface elements and drawings.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文