6

我在我的应用程序中有一个视图,其中包含要显示的巨大数据表。数据显示在两个嵌套的 UniformGrid 中。UniformGrid 是 ItemsControls 中的 ItemPanel,并绑定到一些 ViewModel。请参阅下图和一些示例 XAML 代码:

视图和视图模型 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 的性能优化。

4

2 回答 2

8

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

  • 资源字典
  • 绑定

资源字典

如果您不小心使用基本的 WPF 功能,这些可能会成为主要瓶颈。

ItemsControl就我而言,对于包含 my的单个逻辑滚动,我在 ResourceDictionary 上DataGrids收到了大约 1000 万次调用。处理时间超过 1 秒。GetValueGetValueWithoutLock

访问的主要数量ResourceDictionary是由不同的来源引起的:

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

绑定

绑定非常有用,但在内存和性能方面非常昂贵。在一个非常明智的环境中——从性能的角度来看——你不能不小心就使用它们。例如,绑定可以为每个绑定对象创建最多 3 个 WeakReference,可以使用反射等。我最终在 DataContext 的 PropertyChanged 事件上删除了几乎所有绑定和插入的事件处理程序。当DataContext我的控件(你有一个DataContextChanged事件)发生变化时,我会测试我是否DataContext支持INotifyPropertyChanged,如果是这样,我会在事件上附加一个事件处理程序PropertyChanged并根据需要更新属性。(我只有一种方式绑定,所以这就是我选择这种方式的原因,但还有其他解决方案)。

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

最后一件事:如果你有Freezable对象(例如画笔),Freeze如果可以的话,不要犹豫。

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

祝你好运 !

于 2012-12-20T10:50:24.130 回答
4

如果您确定所有框都希望大小相同,则始终可以创建自己的UniformGrid类并覆盖MeasureOverride以仅测量一个孩子。在我当前的项目中,我最终可能会得到一些具有相同大小内部控件的大型 UniformGrid,所以你的想法引起了我的兴趣,我决定很快尝试一下:

我使用 Petzold 的UniformGridAlmost类作为灵感,但是从而UniformGrid不是从子类化,Panel因此不需要复制 Rows 和 Columns 依赖属性。UniformGrid似乎没有向其派生类公开计算的行数和列数,因此我从Silverlight 的 UniformGrid 类中借用了一个实现并使用了该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 建议中获得更多信息。

于 2012-12-20T18:12:11.777 回答