如何处理巨大的UniformGrids中的性能?

时间:2012-12-14 10:38:13

标签: wpf performance xaml

我的应用程序中有一个视图,可以显示巨大的数据表。数据显示在两个嵌套的UniformGrids中。 UniformGrids是ItemsControls中的ItemPanels,并绑定到某些ViewModel。请参阅下图和一些示例XAML-Code:

view and viewmodel http://img593.imageshack.us/img593/8825/stackoverflowuniformgri.png

<!-- The green boxes -->
<ItemsControl ItemsSource="{Binding BigCells}">

  <ItemsControl.ItemPanel>
    <PanelTemplate>
      <UniformGrid />
    </PanelTemplate>
  </ItemsControl.ItemPanel>

  <ItemsControl.ItemTemplate>
    <DataTemplate>

      <!-- The blue boxes -->
      <ItemsControl ItemsSource="{Binding SmallCells}">
        <ItemsControl.ItemPanel>
          <PanelTemplate>
            <UniformGrid />
          </PanelTemplate>
        </ItemsControl.ItemPanel>
      </ItemsControl>

    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

现在,我希望我的视图可以调整大小,但由于计算了每个小盒子的布局,因此它的性能不佳。
这至少对于盒子大小只能进行一次,因为它对于所有盒子都是相同的。

在WPF中显示大量控件的最佳做法是什么?我可以从哪里开始优化?关键字已经帮助我继续发现WPF中UniformGrids的性能优化。

2 个答案:

答案 0 :(得分:8)

我在同时使用大量WPF DataGrid时遇到了类似的问题。它最终归结为两个主要问题:

  • 资源词典
  • 绑定

资源词典

如果您使用基本的WPF功能而不小心,那些可能是主要瓶颈。

在我的情况下,对于ItemsControl中包含我的DataGrids的单个逻辑滚动条,我收到了大约1000万次调用GetValueGetValueWithoutLock的内容ResourceDictionary中。处理超过1秒。

ResourceDictionary次访问的主要数量来自不同的来源:

  • 动态资源检索:如果您有ControlTemplates,一般资源放在某些资源词典中,并且您通过{DynamicResource ...}访问它们,请将其删除。在某处有一些静态并直接访问它们。
  • 样式获取:如果您使用的不同Visual上没有样式,或者如果您有样式但未设置FrameworkElement.OverridesDefaultStyle属性,WPF将尝试在所有中找到与控件匹配的样式资源,导致大量访问资源字典。为避免这种情况,请务必覆盖控件的所有控件模板,并在每个控件上设置Style={x:Null}(如果需要控件样式,请在控件上设置内联,并添加{{ 1}})
  • 隐式DataTemplates:隐式数据模板非常有用,但远非免费。在查找要应用的OverridesDefaultStyle = true时,WPF将再次浏览您的资源字典,以查找与您的ViewModel类型匹配的DataTemplate。解决方案是为绑定到ViewModel的每个控件使用DataTemplate选择器,并实现优化的方法来检索正确的DataTemplate。 (我个人有一些静态字段用于我的dataTemplate,我只从resourceDictionary中检索一次,以及一个优化的DataTemplate,根据需要返回它们。)

<强>绑定

绑定非常有用,但非常昂贵,内存明智且性能明智。在一个非常明智的环境中 - 从性能的角度来看 - 你不能只是在不小心的情况下使用它们。例如,绑定可以为每个绑定对象创建最多3个WeakReferences,可以使用反射等。我最终在我的DataContext的PropertyChanged事件中删除了几乎所有绑定和插入事件处理程序。当DataTemplateSelector更改我的控件(您有DataContext事件)时,我会测试DataContextChanged是否支持DataContext,如果是这种情况,我会在{{{}}附加一个事件处理程序根据需要{1}}事件和更新属性。 (我只有一种方式绑定,所以这就是我选择这种做法的原因,但还有其他方法)。

使用MVVM和WPF时不使用绑定似乎令人沮丧,但在我的情况下实际上没有办法优化绑定。

最后一件事:如果你有INotifyPropertyChanged个对象(比如画笔),如果可以,请不要犹豫PropertyChanged

这些是可以帮助您优化代码的一些建议。您将需要一个分析器(我总是建议使用dotTrace,因为这是我曾经使用过的最好的分析器)来监控真实情况并根据结果进行调整。

祝你好运!

答案 1 :(得分:4)

如果你确定所有想要大小相同的方框,你可以随时制作自己的UniformGrid课程并覆盖MeasureOverride来衡量一个孩子。在我当前的项目中,我最终可能会得到一些具有相同大小内部控件的大型UniformGrids,所以你的想法引起了我的兴趣,我决定快速尝试一下:

我使用Petzold的UniformGridAlmost类作为灵感,但是从UniformGrid而不是Panel进行了子类化,因此不需要复制Rows和Columns depdendency属性。 UniformGrid似乎没有公开计算出的派生类的行数和列数,因此我从Silverlight's UniformGrid class借用了一个实现并使用了UpdateComputedValues方法。

我班上的MeasureOverride看起来像这样

  protected override Size MeasureOverride(Size sizeAvailable)
  {
     if (InternalChildren.Count == 0)
     {
        return new Size(0, 0);
     }

     UpdateComputedValues();

     // Calculate a child size based on uniform rows and columns.
     Size sizeChild = new Size(sizeAvailable.Width / ComputedColumns,
                               sizeAvailable.Height / ComputedRows);

     // Assume children will measure to at least a comparable size.
     UIElement child = InternalChildren[0];
     child.Measure(sizeChild);

     double width = Math.Max(width, child.DesiredSize.Width);
     double height = Math.Max(height, child.DesiredSize.Height);
     return new Size(ComputedColumns * width, ComputedRows * height);
  }

所以,这是踢球者,当我在带有10000个TextBlocks的ItemsControl上测试它时,我的UniformGrid版本更快,但只有很少量(小于1% - 我确认对MeasureOverride的调用大约是95%快点)。所以这可能没有帮助,但我想我会分享我的经验。您的观察结果是不需要测量每个方框以确定您的情况下的统一布局是正确的,但这种测量并不会让它陷入困境。我确信这是因为所有这些控制仍然需要绘制,所以无论如何它们将在稍后进行测量。您最有可能从ResourceDictionary / Binding建议中获得更多信息。