Xceed DataGrid for WPF v7.3 Documentation
In This Topic
    Custom statistical function
    In This Topic

    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.

    XAML
    Copy Code
    <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>
    VB.NET
    Copy Code
    Imports System
    Imports System.Collections.ObjectModel
    Imports System.Collections.Specialized
    Imports System.Text.RegularExpressions
    Imports Xceed.Wpf.DataGrid.Stats
    Namespace Xceed.Wpf.Documentation
      ' This statistical function derives from CumulativeStatFunction because it can
      ' accumulate "partial" results. For instance, results of sub-group. This allows
      ' for better performance.
      Public Class CountIfFunction
        Inherits CumulativeStatFunction
        ' A parameterless constructor is necessary to use the class in XAML.
        Public Sub New()
          MyBase.New()
          m_conditions = New ObservableCollection(Of String)()
          AddHandler m_conditions.CollectionChanged, AddressOf m_conditions_CollectionChanged
        End Sub
        ' Initialize a new instance of the CountIfFunction specifying the ResultPropertyName
        ' and the SourcePropertyName.
        Public Sub New(ByVal resultPropertyName As String, ByVal sourcePropertyNames As String)
          MyBase.New(resultPropertyName, sourcePropertyNames)
          m_conditions = New ObservableCollection(Of String)()
          AddHandler m_conditions.CollectionChanged, AddressOf m_conditions_CollectionChanged
        End Sub
        ' Each condition applies to the values of the corresponding source property name
        ' (e.g., the first condition applies to the values of the first source property name).
        ' Gets the conditions that will be applied to the various values.
        Public ReadOnly Property Conditions() As ObservableCollection(Of String)
          Get
            Return m_conditions
          End Get
        End Property
        ' When the grid needs to create temporary CountIfFunction instances for its
        ' calculation, this method will be called. Be sure to initialize everything
        ' having an impact on the result here.
        Protected Overrides Sub InitializeFromInstance(ByVal source As StatFunction)
          MyBase.InitializeFromInstance(source)
          For Each condition As String In (CType(source, CountIfFunction)).Conditions
            Me.Conditions.Add(condition)
          Next condition
        End Sub
        ' Validate the CountIf statistical function to make sure that it is capable
        ' to calculate its result. In our case, we need to make sure that a ResultPropertyName
        ' has been specified and that we have the same number of source property names
        ' as conditions.
        Protected Overrides Sub Validate()
          If (Me.ResultPropertyName Is Nothing) OrElse
             (Me.ResultPropertyName = String.Empty) OrElse
             (m_conditions.Count <> Me.SourcePropertyName.Split(","c).Length) Then
            Throw New InvalidOperationException()
          End If
        End Sub
        ' This method will be called when a new calculation is about to begin.
        Protected Overrides Sub Reset()
          m_count = 0
        End Sub
        ' This method will be called for each data item that is part of the set (a group or
        ' the grid).
        Protected Overrides Sub Accumulate(ByVal values As Object())
          Dim i As Integer = 0
          Do While i < m_conditions.Count
            ' As soon as one condition does not match is associated value, we simply
            ' return without having done the accumulation (the count increment).
            If (Not Regex.IsMatch(values(i).ToString(), m_conditions(i))) Then
              Return
            End If
            i += 1
          Loop
          ' The compilation configuration will cause this line to throw
          ' if an OverflowException occurs.
          m_count += 1
        End Sub
        ' This method will be called when calculating the result of a group having
        ' sub-groups. Obviously, it will be called once for each sub-group.
        Protected Overrides Sub AccumulateChildResult(ByVal childResult As StatResult)
          m_count += Convert.ToInt64(childResult.Value)
        End Sub
        ' This method should return the result calculated so far.
        Protected Overrides Function GetResult() As StatResult
          Return New StatResult(m_count)
        End Function
        ' The addition of the Conditions property, which influences the result of the
        ' statistical function, the CountIf function requires us to override IsEquivalent
        ' and GetEquivalenceKey to return a new key when 2 instances are compared.
        Protected Overrides Function IsEquivalent(ByVal statFunction As StatFunction) As Boolean
          Dim countIfFunction As CountIfFunction = TryCast(statFunction, CountIfFunction)
          If countIfFunction Is Nothing Then
            Return False
          End If
          If m_conditions.Count <> countIfFunction.Conditions.Count Then
            Return False
          End If
          Dim i As Integer = 0
          Do While i < m_conditions.Count
            If m_conditions(i) <> countIfFunction.Conditions(i) Then
              Return False
            End If
            i += 1
          Loop
          Return MyBase.IsEquivalent(statFunction)
        End Function
        Protected Overrides Function GetEquivalenceKey() As Integer
          Dim hashCode As Integer = MyBase.GetEquivalenceKey()
          Dim i As Integer = 0
          Do While i < m_conditions.Count
            hashCode = hashCode Xor m_conditions(i).GetHashCode()
            i += 1
          Loop
          Return hashCode
        End Function
        ' Do not allow the Conditions property to be changed if the statistical function has
        ' been sealed (i.e, assigned to the DataGridCollectionView.StatFunctions property).
        Private Sub m_conditions_CollectionChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
          Me.CheckSealed()
        End Sub
        Private m_conditions As ObservableCollection(Of String)
        Private m_count As Long
      End Class
    End Namespace
    C#
    Copy Code
    using System;
    using Xceed.Wpf.DataGrid.Stats;
    using System.Text.RegularExpressions;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    namespace Xceed.Wpf.Documentation
    {
     // This statistical function derives from CumulativeStatFunction because it can
     // accumulate "partial" results. For instance, results of sub-group. This allows
     // for better performance.
     public class CountIfFunction : CumulativeStatFunction
     {
       // A parameterless constructor is necessary to use the class in XAML.
       public CountIfFunction()
         : base()
       {
         m_conditions = new ObservableCollection<string>();
         m_conditions.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( m_conditions_CollectionChanged );
       }
       // Initialize a new instance of the CountIfFunction specifying the ResultPropertyName
       // and the SourcePropertyName.
       public CountIfFunction( string resultPropertyName, string sourcePropertyNames )
         : base( resultPropertyName, sourcePropertyNames )
       {
         m_conditions = new ObservableCollection<string>();
         m_conditions.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( m_conditions_CollectionChanged );
       }
       // Each condition applies to the values of the corresponding source property name
       // (e.g., the first condition applies to the values of the first source property name).
       // Gets the conditions that will be applied to the various values.
       public ObservableCollection<string> Conditions
       {
         get
         {
           return m_conditions;
         }
       }
       // When the grid needs to create temporary CountIfFunction instances for its
       // calculation, this method will be called. Be sure to initialize everything
       // having an impact on the result here.
       protected override void InitializeFromInstance( StatFunction source )
       {
         base.InitializeFromInstance( source );
         foreach( string condition in ( ( CountIfFunction )source ).Conditions )
           this.Conditions.Add( condition );
       }
       // Validate the CountIf statistical function to make sure that it is capable
       // to calculate its result. In our case, we need to make sure that a ResultPropertyName
       // has been specified and that we have the same number of source property names
       // as conditions.
       protected override void Validate()
       {
         if( ( string.IsNullOrEmpty( ResultPropertyName ) ) ||
             ( m_conditions.Count != this.SourcePropertyName.Split( ',' ).Length ) )
         {
           throw new InvalidOperationException();
         }
       }
       // This method will be called when a new calculation is about to begin.
       protected override void Reset()
       {
         m_count = 0;
       }
       // This method will be called for each data item that is part of the set (a group or
       // the grid).
       protected override void Accumulate( object[] values )
       {
         for( int i = 0; i < m_conditions.Count; i++ )
         {
           // As soon as one condition does not match is associated value, we simply
           // return without having done the accumulation (the count increment).
           if( !Regex.IsMatch( values[ i ].ToString(), m_conditions[ i ] ) )
             return;
         }
         // In case of an overflow, we want to stop the calculation and report the error.
         checked
         {
           m_count++;
         }
       }
       // This method will be called when calculating the result of a group having
       // sub-groups. Obviously, it will be called once for each sub-group.
       protected override void AccumulateChildResult( StatResult childResult )
       {
         checked
         {
           m_count += Convert.ToInt64( childResult.Value );
         }
       }
       // This method should return the result calculated so far.
       protected override StatResult GetResult()
       {
         return new StatResult( m_count );
       }
       // The addition of the Conditions property, which influences the result of the
       // statistical function, the CountIf function requires us to override IsEquivalent
       // and GetEquivalenceKey to return a new key when 2 instances are compared.
       protected override bool IsEquivalent( StatFunction statFunction )
       {
         CountIfFunction countIfFunction = statFunction as CountIfFunction;
         if( countIfFunction == null )
           return false;
         if( m_conditions.Count != countIfFunction.Conditions.Count )
           return false;
         for( int i = 0; i < m_conditions.Count; i++ )
         {
           if( m_conditions[ i ] != countIfFunction.Conditions[ i ] )
             return false;
         }
         return base.IsEquivalent( statFunction );
       }
       protected override int GetEquivalenceKey()
       {
         int hashCode = base.GetEquivalenceKey();
         for( int i = 0; i < m_conditions.Count; i++ )
           hashCode ^= m_conditions[ i ].GetHashCode();
         return hashCode;
       }
       // Do not allow the Conditions property to be changed if the statistical function has
       // been sealed (i.e., assigned to the DataGridCollectionView.StatFunctions property).
       private void m_conditions_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
       {
         this.CheckSealed();
       }
       private ObservableCollection<string> m_conditions;
       private long m_count;
     }
    }