WPF 文本块性能较差
当显示少量数据时,我一直遇到 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
TextBox 和 Rectangle 之间的性能差异是由于这些控件的复杂性不同造成的。只需比较生成的视觉树的复杂性(即使用 XamlPad)。矩形很可能只知道其所需的大小。另一方面,文本框在计算所需大小时需要考虑许多不同的因素,例如实际文本的所需大小(我猜这是真正的瓶颈)。
话虽如此,您可能想尝试一些优化。测量过程的目的是确定您所需的尺寸。此外,您还可以通过在所有子元素上调用measure来传播measure pass。但是,仅当您期望更改所需大小时才需要执行此操作。看来您对具有 _rowHeight 和 _columnWidth 字段的布局了解很多。因此,请执行以下操作:
child.Measure(new Size(_columnWidth, _rowHeight))
测量您的孩子。这是实际的约束吗?numberOfColumns * _columnWidth...
)但是,此方法不考虑 TextBox 元素的所需大小。您可以不关心或单独解决这个问题:
除了改进 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:
child.Measure(new Size(_columnWidth, _rowHeight))
. This is the actual constraint right?numberOfColumns * _columnWidth...
)This approach, however, does not respect the desired size of the TextBox elements. You can either not care or solve this separately:
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.
首先,在测试您的代码后,您似乎已经重写了
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 yourTestPanel
with theWrapPanel
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 thanTextBlock
, however, so I would encourage you to think about what it is you are trying to accomplish before switching toFormattedText
.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 aListView
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.