Optimizing Infragistics XamDataGrid Performance

[Infragistics] Kiril Matev / Tuesday, October 26, 2010

The XamDataGrid component, one of the controls in the NetAdvantage for WPF Line of Business product (free trial version available here) is the most frequently-used component from our WPF product. It presents your tabular data, and allows you to group, sort, filter, compute column summaries, as well as control its layout by column grouping, reordering, pinning and many more. The XamDataGrid is also capable of handling real-time data updates and large volumes of data efficiently. In such scenarios, XamDataGrid performance is critical to the usability of your application. This is why in this post, we’ll look at what you can do to maximize its performance. If you'd like to find out how to improve XamDataGrid initial startup time, please see this post.

Sample Project

Please download the sample project featuring the XamDataGrid to see some of the performance optimizations described in this post implemented. The project is built using Visual Studio 2010, and includes trial versions of the libraries you will need to build and run it. You can download a fully-functional free trial NetAdvantage for WPF Line of Business 11.1, which also includes support during the evaluation.

Here’s a screenshot of the sample application:

 

Please follow the instructions on the left and go through the scenarios described to become aware of how the settings affect XamDataGrid performance.

Built-in optimizations

The XamDataGrid uses a variety of methods internally to make sure you’re getting the best performance – it reuses presenter elements at both the record and cell level, caches style information, uses deferred tooltip scrolling and lazy object creation. A more specific information on performance optimizations is available. However, all of these optimizations have already been built into the control and come out-of-the box at no cost.

So, what are the ways you can improve performance? I’ve put together a list of points you might consider using, most of which are implemented in the sample project. I’ve tried to provide as much information on how these strategies impact how your application looks or behaves, so you can consider your scenario’s specific requirements and make an informed decision about which ones to use.

Binding to pre-formatted values

One way to improve performance in read-only columns is to reduce value formatting. Often value formatting is implemented using convertors, because it's an easy and clean way to encapsulate formatting logic. However, when scrolling, the grid causes the converter logic to be invoked each time a cell is brought into view - when scrolling horizontally to bring a new column into view, the converter is invoked a number of times equal to the number of visible rows. The overhead of this processing is increased when using real-time updates. One way to reduce the load is to perform the value formatting in code, and bind the grid to the formatted result, which is computed just once. Furthermore, using a lazy approach, a value can be formatted once and then cached, essentially removing any need for formatting to be triggered by the UI. 

Let's say you have a Product business object with a Price value you'd like to format in a specific way. One way to do that is to extend the original Product using a FormattedProduct, which adds a PriceFormatted property, which is computed when it is requested, and stored for later requests. If there's a relative small number of unique values, one way to further reduce the space requirement of this caching approach is to use a dictionary for formatted values.

Then, once you bind to a list of these FormattedProducts, you need to prevent the Price column from being shown, and to change the header of the PriceFormatted column, which will show its formatted values. You can do that in the event handler of the FieldLayoutInitialized event, where you can set the Visibility of the Price column to Collapsed, and the Label property of the PriceFormatted to 'Price'.

Styling

Hoverless Styles

A substantial performance boost can be achieved by setting a style which refrains from applying coloring and effects in the most-frequently used visual elements. One such example is not setting any coloring to the row being hovered. Please download the high-performance XamDataGrid hoverless style. Of course, you might want to keep using some of these styling properties, and you may modify it to suit your needs. Add ths file to your project, and reference it from the XamDataGrid as follows:

<igDP:XamDataGrid.Resources>
<ResourceDictionary Source="HoverlessStyles.xaml" />
</igDP:XamDataGrid.Resources>

Minimalist Styles

When you’re defining your own templates for elements which appear many times in the visual hierarchy, such as CellValuePresenters, try and use as few elements as possible. A template will be initialized for all CellValuePresenters in view, which can cause the total number of visual elements to increase rapidly, having an adverse effect on performance. A more detailed explanation on the impact of styles on performance is available. The minimal CellValuePresenter consists of a Border enclosing a TextBlock, and is available for download here. An important detail needed to improve performance when using the minimalist style is to set the ForceCellVirtualization property (new in 11.1) to true. Add this minimalist style file to your project, and reference it from the XamDataGrid as follows:

<igDP:XamDataGrid.Resources>
<ResourceDictionary Source="ReadOnlyHighPerformanceStyle.xaml" />
</igDP:XamDataGrid.Resources>

Effect of Containing controls on XamDataGrid Performance

Please refrain from using Bitmap Effects in controls containing the XamDataGrid, or in XamDataGrid elements. However appealing, such effects come at a significant performance cost.

If you place the XamDataGrid in a StackPanel control, it will have an infinite size region to display its records, which will cause it to render CellValuePresenters for all cells. Although scrolling may be correctly handled by the enclosing StackPanel, the memory footprint due to the large number of presenters will be significant.

Theming

There is a certain amount of overhead involved in determining the templates to be used for various elements. You can derive a performance benefit by setting the theme to be used, even if it’s the default Aero theme, as shown below:

<igDP:XamDataGrid Theme="Aero" />

Updating the FieldCollection

If you are adding or removing fields from the Fields collection of a FieldLayout with elements visible in the data presenter, this could adversely impact performance. When making multiple changes to the collection, always call BeginUpdate before these changes and EndUpdate after the changes.

Suppressed Events

Another way developers can improve performance is by suppressing routed events they know they will not be handling. This improves performance due to the overhead incurred with routed events in element hierarchies. There are also direct CLR events added for common routed events on the DataPresenterBase. They are named with the name of the routed event followed by "Direct", such as "CellActivatedDirect", in the case of the "CellActivated" routed event. This allows you to suppress the routed event and still handle its direct equivalent.

In order to suppress events, add them to the DataPresenterBase.SuppressedEvents collection, as implemented in the sample project and also shown below:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:igDP="http://infragistics.com/DataPresenter"
    xmlns:igED="http://infragistics.com/Editors"
    Title="Window1" Height="600" Width="800">
    <Grid>
        <igDP:XamDataGrid>
            <igDP:XamDataGrid.SuppressedEvents>
                <igDP:RoutedEventWrapper RoutedEvent="igED:ValueEditor.TextChanged" />


                <igDP:RoutedEventWrapper RoutedEvent="igED:ValueEditor.ValueChanged" />
            </igDP:XamDataGrid.SuppressedEvents>
        </igDP:XamDataGrid>
    </Grid>
</Window>

Record Sizing Mode

The FieldLayoutSettings.DataRecordSizing mode property controls the way the records in the XamDataGrid are sized and resized. This property has an effect on performance because it controls whether each row is sized separately or the rows are of equal height.

The Fixed and SizableSynchronized values for this property result in rows of equal height. These two settings offer the biggest performance advantage, as the XamDataGrid can layout equi-height rows quicker than rows of different height, which require additional resources during the layout operation.

The SizedToContentAndFixed and SizedToContentAndIndividuallySizable values for the FieldLayoutSettings.DataRecordSizing may result in rows of different size. Although these settings offer a certain amount of flexibility in layout, they require additional resources in the layout, thus having a negative impact the XamDataGrid performance when scrolling.

Setting Scrolling Mode

The default thumb scrolling behavior of the XamDataGrid is deferred scrolling with a tooltip containing the column value (which you can specify using the IsScrollTipField property) for the record that will be brought into view. This allows you to quickly locate the record you’re looking for using its value in a particular column, rather than pause scrolling to look for the current record column value.

You can enable immediate scrolling by setting the ScrollingMode property to Immediate. Unlike deferred scrolling, immediate scrolling requires that the records at the corresponding scroll position are shown in their entirety. This approach results in the manipulation of many visual elements in rapid succession, negatively impacting performance. Because of this, please refrain from using immediate scrolling mode as much as possible, unless you have a specific requirement to do so.

Adjusting Cell Presenter Virtualization

You can control how the XamDataGrid virtualizes its record and cell presenters, by setting the RecordContainerGenerationMode and CellContainerGenerationMode properties. We’ll go through the different virtualization strategies, their space and time implications, and the scenarios they’re useful for.

Recycle

By default, the XamDataGrid recycles the presenters it uses – it only initializes presenters for the visible records/cells which are then reused as the view is scrolled. This mode of caching is optimized for general browsing of data minimizing the memory footprint, at the expense of the time aspect. Although this approach conserves memory and works well in most cases, it can result in less than perfect scrolling, especially with editors such as XamDateTimeEditor containing a complex element hierarchy.

PreLoad

Let’s imagine a scenario where you have many columns with complex editors such as XamDateTimeEditors, and you require quick horizontal scrolling to bring any extra columns into view. Using the PreLoad strategy, the XamDataGrid initializes presenters for all the cells to be shown ensuring a smooth scrolling experience, at the cost of an increased initial loading time and memory footprint. The PreLoad setting is appropriate for scenarios where you have a relatively small number of rows (when setting RecordContainerGenerationMode) or columns (when setting CellContainerGenerationMode), otherwise the loading time and memory footprint can become noticeable.

LazyLoad

You may be implementing a scenario where you have a relatively small amount of records, and a large amount of columns, and the user does not always look through all the columns. In this case, you can minimize the memory footprint while ensuring a good scrolling speed for columns that have already been brought into view by initializing presenters for columns only once – when they are brought into view, and caching them to improve scrolling once these are brought into view for a second time. This results in a small initial loading time, a memory footprint containing presenters for all cells that have been brought into view since the loading of the XamDataGrid (not all, as in the case of the PreLoad strategy), and a scrolling performance penalty only the first time a cell is brought into view. Bringing the same cells into view a second time will be fast, as their presenters would have been cached the first time they were shown.

Hierarchies

In order to improve performance, the XamDataGrid by default uses a single panel for displaying parent and child records. This has the following drawbacks:

  • Nested records are not surrounded by chrome
  • Triggers in a RecordPresenter object’s template dealing with nested records will be ignored
  • Animation in a RecordPresenter object’s template dealing with nested records will be ignored

If you would like to use any of the features listed above, you can use multiple nested panels by setting the UseNestedPanels property of the GridViewSettings to true. Additional information on hierarchical records and performance is available.

Summary

In this blog post, we looked at a number of ways to fine-tune XamDataGrid performance. Please consider and use these next time you’re building your application, keeping in mind your theming, styling requirements, as well as your memory and time constraints.

If you have any questions, do not hesitate to email me at kmatev@infragistics.com.