Skip to main content

Creating Statistical Functions

When creating a custom statistical function, it is important to analyze how the result is to be calculated in order to achieve the best possible performance. For a given set of data, if a statistical function result can be calculated from the result of the subsets of data forming all the data, the function can derive from CumulativeStatFunction and take advantage of the performance enhancements it offers. If the results cannot be calculated from a subset of data, then you can derive directly from StatFunction.

Accumulating Values

A statistical function's Accumulate method is called—as many times as necessary—to accumulate the values required to calculate its result. The AccumulateChildResult method, rather than the Accumulate method, will be called if the statistical function requires the result of a child group in order to calculate its own result (i.e., derives from CumulativeStatFunction). If it is not possible to accumulate child results (e.g., the statistical function is accumulating values on a last level group), the Accumulate method will be called instead for every data item in the group.

The PrerequisiteFunctions property contains a list of statistical functions whose results may be required by the current function in order to calculate its own result. When a prerequisite function is present, the internal engine will search for an equivalent statistical function (see GetEquivalenceKey and IsEquivalent methods) that has the same SourcePropertyName and will use the result of that function if one is found. If there are no statistical functions that have the same SourcePropertyName, it will calculate it and add it to the pool of results for a future use, if need be. In other words, a prerequisite function defines a prerequisite of nature, not of instance. Using prerequisites may improve performance in certain situations.

When creating a statistical function that does not need child results (i.e., derives directly from StatFunction), its RequiresAccumulation property should be set to true (default) to indicate that it will accumulate its own result. By setting this property to true, the Accumulate or AccumulateChildResult methods will be called—as many times as necessary—to accumulate the values required to calculate the result of the statistical function. If the RequiresAccumulation property is set to false, neither method will be called and only the results of the function's prerequisite functions will be used; setting RequiresAccumulation to false will also improve performance.

Custom statistical functions should call the CheckSealed method before attempting to apply changes that can affect the state of a statistical function. Any public property that is added to a statistical function whose value can affect the result should check to see if the function is sealed before attempting to set the new value. Temporary values accumulated in the statistical function do not need to do this verification.

Initializing and Identifying a Statistical Function

The Initialize method specifies the type in which the values of a statistical function will be accumulated (i.e., the data types corresponding to the SourcePropertyName, in the same order). Typically, Initialize is overridden when the accumulation data type needs to be known in advance rather than verifying it every time Accumulate is called. The InitializeFromInstance method initializes the properties of the current instance of the same-type statistical function when a new instance of the same function needs to be created and should be overridden only if a property that affects the end result is added, in order to include the new property in the initializing process (e.g., the IncludeNullValues property of the CountFunction class). When overriding PrerequisiteFunctions in a derived class, the InitializePrerequisites method must also be overridden to retrieve the results of the prerequisite statistical functions.

Each statistical function is required to provide a unique equivalence key that will return the same value for two statistical functions that would theoretically return the same result for the same values. The "key" is provided through the GetEquivalenceKey method while the IsEquivalent method can be called to verify if the current instance is equivalent to another. Together, these methods can be thought of as the equivalent of the Equals and GetHashCode methods.

note

The IsEquivalent method should be overridden only if a property that affects the end result is added. In this case, GetEquivalenceKey should also be overridden to return a new key. ::

Getting Results

The GetResult method retrieves the result of the statistical function while the Validate method ensures that the function is able to calculate the result by verify that all the required properties have been set and that they contain valid values. If an exception occurs in the Accumulate, AccumulateChildResult, or GetResult methods, it will be caught by a grid and returned as the result of the statistical function. In some cases, such as when the statistical function's required parameters are being validated in the Validate method, the exception will not be caught since if the validation fails the statistical function is not able to calculate its result.

The Reset method resets the statistical function to its original state and will be called every time the result needs to be recalculated.

Examples

All examples in this topic assume that the grid is bound to the Products table of the Northwind database, unless stated otherwise.

Custom statistical function

The following example demonstrates how to create a custom statistical function based on the CountFunction, which will only count the items if they match the specified conditions.

  <Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
xmlns:local="clr-namespace:Xceed.Wpf.Documentation"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<Grid.Resources>
<xcdg:DataGridCollectionViewSource x:Key="cvs_products"
Source="{Binding Source={x:Static Application.Current}, Path=Products}">
<xcdg:DataGridCollectionViewSource.StatFunctions>
<local:CountIfFunction ResultPropertyName="CountProductsToOrder"
SourcePropertyName="ReorderLevel,UnitsOnOrder">
<!-- Only count products which have a ReorderLevel of 5, 10, 15, or 20, and
a UnitsOnOrder value of 0. -->
<local:CountIfFunction.Conditions>
<s:String>^5$|^10$|^15$|^20$</s:String>
<s:String>^0$</s:String>
</local:CountIfFunction.Conditions>
</local:CountIfFunction>
</xcdg:DataGridCollectionViewSource.StatFunctions>
<xcdg:DataGridCollectionViewSource.GroupDescriptions>
<xcdg:DataGridGroupDescription PropertyName="CategoryID" />
</xcdg:DataGridCollectionViewSource.GroupDescriptions>
</xcdg:DataGridCollectionViewSource>
</Grid.Resources>
<xcdg:DataGridControl x:Name="OrdersGrid"
ItemsSource="{Binding Source={StaticResource cvs_products}}">
<xcdg:DataGridControl.Columns>
<xcdg:Column FieldName="ReorderLevel"/>
</xcdg:DataGridControl.Columns>
<xcdg:DataGridControl.DefaultGroupConfiguration>
<xcdg:GroupConfiguration>
<xcdg:GroupConfiguration.Footers>
<DataTemplate>
<xcdg:StatRow Background="Pink">
<xcdg:StatCell FieldName="ReorderLevel"
ResultPropertyName="CountProductsToOrder" />
</xcdg:StatRow>
</DataTemplate>
</xcdg:GroupConfiguration.Footers>
</xcdg:GroupConfiguration>
</xcdg:DataGridControl.DefaultGroupConfiguration>
</xcdg:DataGridControl>
</Grid>