Search code examples
delphitcxgrid

Delphi code to create TcxGrid GroupSummaries at runtime


I have code that creates summary footers at runtime for numeric columns, but I can't get the group summary results to show. I've looked at How to create group summaries at runtime and How to set group summary values and How can create summary footer on runtime? but I'm hitting runtime error:

EcxInvalidDataControllerOperation with message 'RecordIndex out of range'

when the grid is rendering.

This code accepts any TcxGridDBTableView so it would be very easy to put into an existing Delphi form.

procedure SummaryGroup(ASummary: TcxDataSummary; AColumn: TcxGridDBColumn;
  AKind: TcxSummaryKind; AFormat: string);
var
  sumGroup: TcxDataSummaryGroup;
  link: TcxGridTableSummaryGroupItemLink; //TcxDataSummaryGroupItemLink;
  item: TcxGridDBTableSummaryItem;
begin
  AColumn.Summary.FooterKind := AKind;
  AColumn.Summary.FooterFormat := AFormat;
  sumGroup := ASummary.SummaryGroups.Add;
  link := sumGroup.Links.Add as TcxGridTableSummaryGroupItemLink;
  link.Column := AColumn;
  item := sumGroup.SummaryItems.Add as TcxGridDBTableSummaryItem;
  item.Column := AColumn;
  item.Kind := AKind;
  item.Position := spGroup;
  item.Format := AColumn.Summary.FooterFormat;
end;

procedure AutoAwesum(AView: TcxGridDBTableView);
var
  summary: TcxDataSummary;
  summing: Boolean;
  i: Integer;
  dc: TcxGridDBDataController;
  col: TcxGridDBColumn;

begin
  dc := AView.DataController;
  summing := False;
  summary := dc.Summary;
  summary.BeginUpdate;
  try
    summary.SummaryGroups.Clear;
    dc.BeginFullUpdate;
    try
      dc.GridView.ClearItems;
      dc.CreateAllItems;
      for i := 1 to AView.ColumnCount - 1 do
      begin
        col := AView.Columns[i];
        case col.DataBinding.Field.DataType of
          ftSmallint, ftInteger, ftWord, ftLargeint, ftAutoInc,
          ftLongWord, ftShortint:
            begin
              summing := true;
              SummaryGroup(summary, col, skSum, '#');
            end;
          ftFloat, ftBCD, ftFMTBcd, ftExtended, ftSingle:
            begin
              summing := true;
              SummaryGroup(summary, col, skSum, '#.##');
            end;
          ftCurrency:
            begin
              summing := true;
              SummaryGroup(summary, col, skSum, '$#.##');
            end;
        end;
      end;
      dc.DataModeController.GridMode := not summing;
      AView.OptionsView.Footer := summing;
      AView.OptionsView.GroupFooterMultiSummaries := summing;
      AView.OptionsView.GroupFooters := gfVisibleWhenExpanded;
    finally
      dc.EndFullUpdate;
    end;
  finally
    summary.EndUpdate;
  end;
end;

What am I missing? Thanks.


Solution

  • Finally had a chance to get back to this. As expected, the changes were few and simple. Here's the code that generically creates group summary headers for each numeric column in a grid. I've left some options commented out in the code that you may want to use.

    uses
      cxGridDBDataDefinitions;
    
    procedure Summarize(ASummary: TcxDataSummary; AColumn: TcxGridDBColumn;
      AKind: TcxSummaryKind; AFormat: string);
    var
      sumGroup: TcxDataSummaryGroup;
      link: TcxGridTableSummaryGroupItemLink;
      item: TcxGridDBTableSummaryItem;
    begin
      AColumn.Summary.FooterKind := AKind;
      AColumn.Summary.FooterFormat := AFormat;
      AColumn.Summary.GroupKind := AKind;
      AColumn.Summary.GroupFormat := AFormat;
      AColumn.GroupIndex := -1;
      sumGroup := ASummary.SummaryGroups.Add;
      link := sumGroup.Links.Add as TcxGridTableSummaryGroupItemLink;
      link.Column :=  AColumn;
      item := sumGroup.SummaryItems.Add as TcxGridDBTableSummaryItem;
      item.Column := AColumn;
      item.Kind := skSum;
      item.Position := spGroup;
      item.Format := AColumn.Summary.FooterFormat;
    end;
    
    procedure AutoAwesum(AView: TcxGridDBTableView);
    var
      summary: TcxDataSummary;
      summing: Boolean;
      i: Integer;
      dc: TcxGridDBDataController;
      col: TcxGridDBColumn;
    
    begin
      dc := AView.DataController;
      summing := False;
      summary := dc.Summary;
      summary.BeginUpdate;
      try
        summary.SummaryGroups.Clear;
        dc.BeginFullUpdate;
        try
          dc.GridView.ClearItems;
          dc.CreateAllItems;
          for i := 1 to AView.ColumnCount - 1 do
          begin
            col := AView.Columns[i];
            case col.DataBinding.Field.DataType of
              ftSmallint, ftInteger, ftWord, ftLargeint, ftAutoInc,
              ftLongWord, ftShortint:
                begin
                  summing := true;
                  Summarize(summary, col, skSum, ',0');
                end;
              ftFloat, ftBCD, ftFMTBcd, ftExtended, ftSingle:
                begin
                  summing := true;
                  Summarize(summary, col, skSum, ',.00');
                end;
              ftCurrency:
                begin
                  summing := true;
                  Summarize(summary, col, skSum, '$,0.00');
                end;
            end;
          end;
    //      dc.DataModeController.GridMode := not summing;
    //      AView.OptionsView.Header := summing;
          AView.OptionsView.Footer := summing;
    //      AView.OptionsView.GroupFooterMultiSummaries := summing;
    //      AView.OptionsView.GroupFooters := gfVisibleWhenExpanded;
        finally
          dc.EndFullUpdate;
        end;
      finally
        summary.EndUpdate;
      end;
    end;