Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle performance in huge UniformGrids?

I have a view in my application with huge table like data to display. The data is displayed in two nested UniformGrids. The UniformGrids are ItemPanels in ItemsControls and are bound to some ViewModels. See the following image and some example 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>

Now, I want my view to be resizable but this does not perform well at all since every single small box's layout is computed.
This could at least for the boxes size be done only once, since it is equal for all boxes.

What is best practice to display a huge amount of controls in WPF / where could I start optimizing? Keywords would already help me to continue discovering performance optimizations for UniformGrids in WPF.

like image 804
Michael Hilus Avatar asked Dec 14 '12 10:12

Michael Hilus


1 Answers

I've had a similar problem when using a lot of WPF DataGrid at the same time. It finally came down to two major issues :

  • Resource dictionaries
  • Bindings

Resource Dictionaries

Those can be major bottlenecks if you use basic WPF features without being careful.

In my case, for a single logical scroll in the ItemsControl that was containing my DataGrids, I was getting something like 10 millions call to GetValue and GetValueWithoutLock on ResourceDictionary. More than 1 second to process.

This major number of ResourceDictionary accesses was caused by different sources :

  • Dynamic resource retrieval : If you have ControlTemplates, and more generally resources placed in some resource dictionaries, and you access them via {DynamicResource ...}, remove them. Have some static somewhere and acces them directly.
  • Style fetching : If you have no style on the different Visual you use, or if you have a Style but didn't set the FrameworkElement.OverridesDefaultStyle property, WPF will try to find a style matching the control in all the resources, which result in a lot of accesses to resources dictionaries. To avoid that, be sure to override all of the control templates of your controls, and set Style={x:Null} on each and every control (if you need a style for a control, set it inline on your control, and add OverridesDefaultStyle = true)
  • Implicit DataTemplates : Implicit data template are very usefull but are far from free to. When looking for a DataTemplate to apply, WPF will once again browse your resource dictionaries to find a DataTemplate matching your ViewModels types. Solution is to use a DataTemplate selector for each and every control binded to a ViewModel, and implement an optimized way to retrieve the correct DataTemplate. (I personally have some static fields for my dataTemplate that I retrieve only once from a resourceDictionary, and an optimized DataTemplateSelector that returns them as needed.)

Bindings

Bindings are very useful, but very expensive, memory wise and performance wise. In a very sensible environement - from a performance point of view - you can't just use them without being carefull. For exmample, binding can create up to 3 WeakReferences for each binded object, can use reflection, etc. I finally ended up with removing almost all of my binding and plugged event handler on my DataContext's PropertyChanged event. When DataContext changes on my Controls (you have a DataContextChanged event) I test if my DataContext supports INotifyPropertyChanged, and if it's the case, I attach an event handler on PropertyChanged event and update properties as needed. (I only had one way bindings, so this is why I chose this way of doing things, but there are other solutions).

It can seems frustrating not using bindings when using MVVM and WPF, but there was actually no way to optimize bindings in my case.

Last thing : if you have Freezable objects (like Brushes for example), don't hesitate to Freeze them if you can.

Those are some advices that can help you optimizing your code. You will need a profiler (I always advice using dotTrace as this is the best profiler I ever used) to monitor what's really happening and adapt your according to the results.

Good luck !

like image 172
Sisyphe Avatar answered Oct 27 '22 18:10

Sisyphe