Back to Devexpress

Custom Summary

wpf-6129-controls-and-libraries-data-grid-data-summaries-custom-summary.md

latest11.2 KB
Original Source

Custom Summary

  • Aug 25, 2023
  • 4 minutes to read

Total summaries and group summaries contain predefined aggregate functions. These functions allow you to calculate the following:

  • The number of data rows ( Count ).
  • The maximum and minimum values ( Max and Min ).
  • The sum and the average value ( Sum and Average ).

Handle the GridControl.CustomSummary event or use the GridControl.CustomSummaryCommand property to apply custom rules to calculate summaries. Custom summaries allow you to:

  • Calculate summaries against records that meet specific criteria.
  • Involve multiple data fields in calculations.
  • Implement complex summary functions (for example, the standard deviation of a population and so on).

If the GridControl.View property is set to TreeListView, use the TreeListView.CustomSummary event or the TreeListView.CustomSummaryCommand property.

General Information

To calculate a summary manually:

  1. Create a summary item and set its SummaryItemBase.SummaryType property to SummaryItemType.Custom.
  2. Create a command that uses a custom algorithm to calculate summary values.
  3. Bind this command to the GridControl.CustomSummaryCommand property.

The GridControl calculates its summaries as follows:

InitializationThe GridControl executes the CustomSummary command and sets the SummaryArgs.SummaryProcess property to Start. At this stage, you can initialize summary values (for example, reset internal counters).CalculationThe GridControl executes the CustomSummary command multiple times, once for each data row in a View or group. The SummaryArgs.SummaryProcess property is set to Calculate. At this stage, you can calculate summaries.FinalizationThe GridControl executes the CustomSummary command and sets the SummaryArgs.SummaryProcess property to Finalize. At this stage, you can assign the calculated summary to the SummaryArgs.TotalValue property.

To skip the Calculation stage and calculate a custom summary at the Initialization or Finalization stage, set the SummaryArgs.TotalValueReady property to true at the Initialization stage. This skips the Calculation stage and starts the Finalization stage.

Calculate Custom Summaries

The following code sample calculates the total number of empty cells in the specified column:

View Example: How to Use Custom Summaries

xaml
<dxg:GridControl ItemsSource="{Binding Items}"
                 CustomSummaryCommand="{Binding CustomSummaryCommand}">
    <dxg:GridControl.Columns>
        <dxg:GridColumn FieldName="Text" GroupIndex="0" />
        <dxg:GridColumn FieldName="Number" />
    </dxg:GridControl.Columns>
    <dxg:GridControl.View>
        <dxg:TableView AutoWidth="True"
                       NavigationStyle="Cell"
                       TotalSummaryPosition="Bottom" />
    </dxg:GridControl.View>
    <dxg:GridControl.TotalSummary>
        <dxg:GridSummaryItem DisplayFormat="Total empty cells count: {0}"
                             FieldName="Number"
                             SummaryType="Custom" />
    </dxg:GridControl.TotalSummary>
    <dxg:GridControl.GroupSummary>
        <dxg:GridSummaryItem DisplayFormat="Group empty cells count: {0}"
                             FieldName="Number"
                             SummaryType="Custom" />
    </dxg:GridControl.GroupSummary>
</dxg:GridControl>
csharp
using DevExpress.Mvvm;
using DevExpress.Mvvm.DataAnnotations;
using DevExpress.Mvvm.Xpf;
// ...
public class MainViewModel : ViewModelBase {
// ...
    [Command]
    public void CustomSummary(RowSummaryArgs args) {
        if(args.SummaryItem.PropertyName != "Number")
            return;
        if(args.SummaryProcess == SummaryProcess.Start) {
            args.TotalValue = 0;
        } 
        if(args.SummaryProcess == SummaryProcess.Calculate) {
            if(IsEmptyCell(args.FieldValue))
                args.TotalValue = (int)args.TotalValue + 1;
        }
    }
    bool IsEmptyCell(object fieldValue) {
        return !((int?)fieldValue).HasValue;
    }
}
vb
Imports DevExpress.Mvvm
Imports DevExpress.Mvvm.DataAnnotations
Imports DevExpress.Mvvm.Xpf
' ...
Public Class MainViewModel
    Inherits ViewModelBase
' ...
    <Command>
    Public Sub CustomSummary(ByVal args As RowSummaryArgs)
        If Not Equals(args.SummaryItem.PropertyName, "Number") Then Return
        If args.SummaryProcess = SummaryProcess.Start Then
            args.TotalValue = 0
        End If

        If args.SummaryProcess = SummaryProcess.Calculate Then
            If IsEmptyCell(args.FieldValue) Then args.TotalValue = CInt(args.TotalValue) + 1
        End If
    End Sub

    Private Function IsEmptyCell(ByVal fieldValue As Object) As Boolean
        Return Not CType(fieldValue, Integer?).HasValue
    End Function
End Class

Calculate Custom Summaries Based on Predefined Summaries

The GridControl calculates custom summaries after predefined summaries ( Count , Sum , Min , and so on). As a result, you can use predefined summary values to calculate custom summaries:

  1. Create a custom summary.
  2. Handle the GridControl.CustomSummary / TreeListView.CustomSummary event.
  3. At the Initialization stage, set the e.TotalValueReady property to true to skip the Calculation stage.
  4. Use the DataControlBase.GetTotalSummaryValue method to obtain predefined summaries at the Finalization stage.

xaml
<dxg:GridControl ...
                 CustomSummary="grid_CustomSummary">
    <dxg:GridColumn FieldName="ProductName"/>
    <dxg:GridColumn FieldName="UnitPrice"/>
    <dxg:GridColumn FieldName="Quantity"/>
    <dxg:GridControl.TotalSummary>
        <dxg:GridSummaryItem x:Name="avgPrice" FieldName="UnitPrice" SummaryType="Average"/>
        <dxg:GridSummaryItem x:Name="avgQuantity" FieldName="Quantity" SummaryType="Average"/>
        <dxg:GridSummaryItem ShowInColumn="ProductName" SummaryType="Custom" 
                             DisplayFormat="{}Average order: {0:c}"/>
    </dxg:GridControl.TotalSummary>
    <dxg:GridControl.View>
        <dxg:TableView ...
                       TotalSummaryPosition="Bottom">
        </dxg:TableView>
    </dxg:GridControl.View>
</dxg:GridControl>
csharp
private void grid_CustomSummary(object sender, DevExpress.Data.CustomSummaryEventArgs e) {
    if (e.IsTotalSummary) {
        switch (e.SummaryProcess) {
            case DevExpress.Data.CustomSummaryProcess.Start:
                e.TotalValueReady = true;
            break;
            case DevExpress.Data.CustomSummaryProcess.Finalize:
                var averagePrice = (decimal)grid.GetTotalSummaryValue(avgPrice);
                var averageQuantity = (decimal)grid.GetTotalSummaryValue(avgQuantity);
                e.TotalValue = averagePrice * averageQuantity;
            break;
        }
    }
}
vb
Private Sub grid_CustomSummary(ByVal sender As Object, ByVal e As DevExpress.Data.CustomSummaryEventArgs)
    If e.IsTotalSummary Then

        Select Case e.SummaryProcess
            Case DevExpress.Data.CustomSummaryProcess.Start
                e.TotalValueReady = True
            Case DevExpress.Data.CustomSummaryProcess.Finalize
                Dim averagePrice = CDec(grid.GetTotalSummaryValue(avgPrice))
                Dim averageQuantity = CDec(grid.GetTotalSummaryValue(avgQuantity))
                e.TotalValue = averagePrice * averageQuantity
        End Select
    End If
End Sub

You can use the e.GetGroupSummary method to obtain predefined group summary values.

Specify Whether to Calculate Summaries

The CustomSummaryExists event or the CustomSummaryExistsCommand property allows you to specify which summaries should be calculated and displayed.

The following example calculates group summaries only for the top group level:

View Example: How to Display Group Summaries

xaml
<dxg:GridControl x:Name="grid"
                 ItemsSource="{Binding AccountList}"
                 CustomSummaryExistsCommand="{Binding CustomSummaryExistsCommand}">
                 <!-- ... -->
    <dxg:GridControl.GroupSummary>
        <dxg:GridSummaryItem FieldName="Age" SummaryType="Min"/>
        <dxg:GridSummaryItem FieldName="Age" SummaryType="Max"/>
    </dxg:GridControl.GroupSummary>
</dxg:GridControl>
csharp
using DevExpress.Mvvm;
using DevExpress.Mvvm.DataAnnotations;
using DevExpress.Mvvm.Xpf;
// ...
public class MainViewModel : ViewModelBase {
// ...
    [Command]
    public void CustomSummaryExistsCommand(RowSummaryExistsArgs args) {
        args.Exists = args.GroupPath[0].GroupLevel == 0;
    }
}
vb
Imports DevExpress.Mvvm
Imports DevExpress.Mvvm.DataAnnotations
Imports DevExpress.Mvvm.Xpf
' ...
Public Class MainViewModel
    Inherits ViewModelBase
' ...
    <Command>
    Public Sub CustomSummaryExistsCommand(ByVal args As RowSummaryExistsArgs)
        args.Exists = args.GroupPath(0).GroupLevel = 0
    End Sub
End Class