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

Mic*_*lus 5 wpf performance xaml

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

view和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>
Run Code Online (Sandbox Code Playgroud)

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

在WPF中显示大量控件的最佳实践/我可以在哪里开始优化?关键字已经帮助我继续发现WPF中UniformGrids的性能优化.

Sis*_*phe 8

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

  • 资源词典
  • 绑定

资源词典

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

就我而言,在一个单一的逻辑滚动ItemsControl这是包含我的DataGrids,我是越来越像1000万来电GetValue,并GetValueWithoutLock在ResourceDictionary中.处理超过1秒.

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

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

绑定

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

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

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

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

祝好运 !


tor*_*dal 5

如果您确定所有盒子都希望具有相同的尺寸,您始终可以创建自己的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);
  }
Run Code Online (Sandbox Code Playgroud)

因此,关键在于,当我在具有 10000 个 TextBlock 的 ItemsControl 上对其进行测试时,我的 UniformGrid 版本更快,但速度也很小(不到 1% - 我确认对 MeasureOverride 的调用大约快了 95%)。所以这可能没有帮助,但我想我会分享我的经验。您的观察结果是,不需要测量每个盒子来确定您案例中的统一布局,这是正确的,但测量并不是阻碍它的原因。我确信这是因为所有这些控件仍然需要绘制,所以无论如何它们都会在稍后进行测量。您很可能会从 ResourceDictionary/Binding 建议中获得更多信息。