private static DataGridViewRow[][] CollectSummaryGroups(DataGridView grid, SummaryRowPosition position, DataGridViewColumn groupFrom, DataGridViewColumn groupTo)
        {
            List <DataGridViewRow>   group  = new List <DataGridViewRow>();
            List <DataGridViewRow[]> groups = new List <DataGridViewRow[]>();

            var             rows     = grid.Rows;
            DataGridViewRow groupRow = null;

            foreach (DataGridViewRow r in rows)
            {
                // skip hidden rows that are not a child row.
                if (!r.Visible && !r.IsChild)
                {
                    continue;
                }

                if (r is DataGridViewSummaryRow)
                {
                    if (position == SummaryRowPosition.Parent)
                    {
                        if (group.Count > 0)
                        {
                            groups.Add(group.ToArray());
                            group.Clear();
                            groupRow = null;
                        }
                    }

                    continue;
                }

                // first row in group?
                if (groupRow == null)
                {
                    groupRow = r;
                    group.Add(r);
                }
                else if (IsGroupBreak(groupRow, r, groupFrom, groupTo))
                {
                    // create a new group.
                    groups.Add(group.ToArray());
                    group.Clear();
                    groupRow = r;
                    group.Add(r);
                }
                else
                {
                    // add to current group.
                    group.Add(r);
                }
            }

            if (group.Count > 0)
            {
                groups.Add(group.ToArray());
            }

            return(groups.ToArray());
        }
 internal bool Match(DataGridViewRow[] group, SummaryRowPosition summaryPosition, DataGridViewColumn groupFromCol, DataGridViewColumn groupToCol)
 {
     return
         (this.SummaryPosition == summaryPosition &&
          (groupToCol == null || this.GroupToColumn == groupToCol) &&
          (groupFromCol == null || this.GroupFromColumn == groupFromCol) &&
          MatchGroupRows(group));
 }
 private static DataGridViewSummaryRow RetrieveSummaryRow(
     DataGridView grid,
     DataGridViewRow[] group,
     SummaryRowPosition summaryPosition,
     DataGridViewColumn groupFromCol, DataGridViewColumn groupToCol)
 {
     return((DataGridViewSummaryRow)grid.Rows
            .FirstOrDefault(r => (r as DataGridViewSummaryRow)?.Match(group, summaryPosition, groupFromCol, groupToCol) ?? false));
 }
 private static DataGridViewRow[][] FindSummaryGroups(
     DataGridView grid,
     SummaryRowPosition position,
     DataGridViewColumn groupFrom,
     DataGridViewColumn groupTo)
 {
     return(grid.Rows
            .Where(r => (r as DataGridViewSummaryRow)?.Match(null, position, groupFrom, groupTo) ?? false)
            .Select(r => ((DataGridViewSummaryRow)r).GroupRows).ToArray());
 }
 /// <summary>
 /// Removes the summary rows that match the specified <paramref name="summaryPosition"/> and grouped by
 /// <paramref name="groupFromCol"/> and <paramref name="groupToCol"/>.
 /// </summary>
 /// <param name="grid">Target <see cref="DataGridView"/>.</param>
 /// <param name="summaryPosition">Position of the summary rows to remove.</param>
 /// <param name="groupFromCol">First column that determnines the group break values.</param>
 /// <param name="groupToCol">Last column that determines the group break values.</param>
 public static void RemoveSummaryRows(this DataGridView grid, SummaryRowPosition summaryPosition, DataGridViewColumn groupFromCol, DataGridViewColumn groupToCol)
 {
     lock (grid.Rows)
     {
         // clear existing aggregates.
         DataGridViewSummaryRow summaryRow = null;
         while ((summaryRow = RetrieveSummaryRow(grid, null, summaryPosition, groupFromCol, groupToCol)) != null)
         {
             grid.Rows.Remove(summaryRow);
         }
     }
 }
        private static DataGridViewSummaryRow CreateSummaryRow(
            DataGridView grid,
            DataGridViewRow[] group,
            SummaryType summaryType,
            SummaryRowPosition summaryPosition,
            DataGridViewColumn groupFromCol, DataGridViewColumn groupToCol,
            DataGridViewColumn summaryCol,
            DataGridViewCellStyle style)
        {
            if (group.Length > 0)
            {
                // create and add the summary row.
                var summaryRow = new DataGridViewSummaryRow(
                    group,
                    groupFromCol,
                    groupToCol,
                    summaryPosition);

                summaryRow.ReadOnly = true;

                if (style != null)
                {
                    summaryRow.DefaultCellStyle = style;
                }

                switch (summaryPosition)
                {
                case SummaryRowPosition.Above:
                    grid.Rows.Insert(FindInsertIndex(grid, group, summaryPosition), summaryRow);
                    break;

                case SummaryRowPosition.Parent:
                    grid.Rows.Insert(FindInsertIndex(grid, group, summaryPosition), summaryRow);
                    foreach (var r in group)
                    {
                        r.ParentRow = summaryRow;
                    }
                    break;

                case SummaryRowPosition.Below:
                default:
                    grid.Rows.Insert(FindInsertIndex(grid, group, summaryPosition), summaryRow);
                    break;
                }

                DisplayGroupValues(summaryRow);

                return(summaryRow);
            }

            return(null);
        }
        private static int FindInsertIndex(DataGridView grid, DataGridViewRow[] group, SummaryRowPosition summaryPosition)
        {
            var index = 0;

            switch (summaryPosition)
            {
            case SummaryRowPosition.Above:
            {
                index = group[0].Index;
                while (index > 0 &&
                       grid.Rows[index - 1] is DataGridViewSummaryRow &&
                       ((DataGridViewSummaryRow)grid.Rows[index - 1]).SummaryPosition == summaryPosition)
                {
                    index--;
                }
            }
            break;

            case SummaryRowPosition.Below:
            {
                index = group[group.Length - 1].Index + 1;
                while (index < grid.RowCount - 1 &&
                       grid.Rows[index + 1] is DataGridViewSummaryRow &&
                       ((DataGridViewSummaryRow)grid.Rows[index + 1]).SummaryPosition == summaryPosition)
                {
                    index++;
                }
            }
            break;

            case SummaryRowPosition.Parent:
            {
                index = group[0].Index;
            }
            break;
            }

            return(index);
        }
 /// <summary>
 /// Creates or updates a <see cref="DataGridViewSummaryRow"/> for each group limited by the
 /// values in column <paramref name="groupCol"/>.
 /// </summary>
 /// <param name="grid">Extension class.</param>
 /// <param name="summaryType">Determines the aggregation type.</param>
 /// <param name="summaryPosition">Indicates the position of the <see cref="DataGridViewSummaryRow"/>.</param>
 /// <param name="groupCol">Name of the column that determines the group break values.</param>
 /// <param name="summaryCol">Name of the column to aggregate.</param>
 /// <param name="style">Optional <see cref="DataGridViewCellStyle"/> for the summary rows.</param>
 /// <returns>Array of the <see cref="DataGridViewSummaryRow"/> rows displaying the aggregated values.</returns>
 public static DataGridViewSummaryRow[] AddSummaryRows(this DataGridView grid, SummaryType summaryType, SummaryRowPosition summaryPosition, string groupCol, string summaryCol, DataGridViewCellStyle style = null)
 {
     return(AddSummaryRows(
                grid,
                summaryType,
                summaryPosition,
                groupCol,
                groupCol,
                summaryCol,
                style));
 }
 /// <summary>
 /// Removes the summary rows that match the specified <paramref name="summaryPosition"/> and grouped by
 /// <paramref name="groupFromCol"/> and <paramref name="groupToCol"/>.
 /// </summary>
 /// <param name="grid">Target <see cref="DataGridView"/>.</param>
 /// <param name="summaryPosition">Position of the summary rows to remove.</param>
 /// <param name="groupFromCol">Name of the first column that determnines the group break values.</param>
 /// <param name="groupToCol">Name of the last column that determines the group break values.</param>
 public static void RemoveSummaryRows(this DataGridView grid, SummaryRowPosition summaryPosition, string groupFromCol, string groupToCol)
 {
     RemoveSummaryRows(grid, summaryPosition, grid.Columns[groupFromCol], grid.Columns[groupToCol]);
 }
 /// <summary>
 /// Removes the summary rows that match the specified <paramref name="summaryPosition"/>.
 /// </summary>
 /// <param name="grid">Target <see cref="DataGridView"/>.</param>
 /// <param name="summaryPosition">Position of the summary rows to remove.</param>
 public static void RemoveSummaryRows(this DataGridView grid, SummaryRowPosition summaryPosition)
 {
     RemoveSummaryRows(grid, summaryPosition, (DataGridViewColumn)null, (DataGridViewColumn)null);
 }
        /// <summary>
        /// Creates or updates a <see cref="DataGridViewSummaryRow"/> for each group limited by the
        /// values in the columns from <paramref name="groupFromCol"/> to <paramref name="groupToCol"/>.
        /// </summary>
        /// <param name="grid">Extension class.</param>
        /// <param name="summaryType">Determines the aggregation type.</param>
        /// <param name="summaryPosition">Indicates the position of the <see cref="DataGridViewSummaryRow"/>.</param>
        /// <param name="groupFromCol">First column that determines the group break values.</param>
        /// <param name="groupToCol">Last column that determines the group break values.</param>
        /// <param name="summaryCol">Column to aggregate.</param>
        /// <param name="style">Optional <see cref="DataGridViewCellStyle"/> for the summary rows.</param>
        /// <returns>Array of the <see cref="DataGridViewSummaryRow"/> rows displaying the aggregated values.</returns>
        public static DataGridViewSummaryRow[] AddSummaryRows(this DataGridView grid, SummaryType summaryType, SummaryRowPosition summaryPosition, DataGridViewColumn groupFromCol, DataGridViewColumn groupToCol, DataGridViewColumn summaryCol, DataGridViewCellStyle style = null)
        {
            if (summaryType != SummaryType.None && summaryCol == null)
            {
                throw new ArgumentNullException(nameof(summaryCol));
            }

            lock (grid.Rows)
            {
                var groups = FindSummaryGroups(grid, summaryPosition, groupFromCol, groupToCol);
                if (groups.Length == 0)
                {
                    groups = CollectSummaryGroups(grid, summaryPosition, groupFromCol, groupToCol);
                }

                // calculate the specified aggregates.
                var summaryRows = new List <DataGridViewSummaryRow>();
                if (groups.Length > 0)
                {
                    for (int i = 0; i < groups.Length; i++)
                    {
                        // retrieve or create the summary row.
                        var summaryRow =
                            RetrieveSummaryRow(grid, groups[i], summaryPosition, groupFromCol, groupToCol)
                            ?? CreateSummaryRow(grid, groups[i], summaryType, summaryPosition, groupFromCol, groupToCol, summaryCol, style);

                        // calculate the aggregate value.
                        CalculateSummary(summaryRow, summaryType, groups[i], summaryCol);

                        summaryRows.Add(summaryRow);
                    }

                    return(summaryRows.ToArray());
                }
            }

            return(null);
        }
 /// <summary>
 /// Creates or updates a <see cref="DataGridViewSummaryRow"/> for each group limited by the
 /// values in the columns from <paramref name="groupFromCol"/> to <paramref name="groupToCol"/>.
 /// </summary>
 /// <param name="grid">Extension class.</param>
 /// <param name="summaryType">Determines the aggregation type.</param>
 /// <param name="summaryPosition">Indicates the position of the <see cref="DataGridViewSummaryRow"/>.</param>
 /// <param name="groupFromCol">Name of the first column that determines the group break values.</param>
 /// <param name="groupToCol">Name of the last column that determines the group break values.</param>
 /// <param name="summaryCol">Name of the column to aggregate.</param>
 /// <param name="style">Optional <see cref="DataGridViewCellStyle"/> for the summary rows.</param>
 /// <returns>Array of the <see cref="DataGridViewSummaryRow"/> rows displaying the aggregated values.</returns>
 public static DataGridViewSummaryRow[] AddSummaryRows(this DataGridView grid, SummaryType summaryType, SummaryRowPosition summaryPosition, string groupFromCol, string groupToCol, string summaryCol, DataGridViewCellStyle style = null)
 {
     return(AddSummaryRows(
                grid,
                summaryType,
                summaryPosition,
                String.IsNullOrEmpty(groupFromCol) ? null : grid.Columns[groupFromCol],
                String.IsNullOrEmpty(groupToCol) ? null : grid.Columns[groupToCol],
                String.IsNullOrEmpty(summaryCol) ? null : grid.Columns[summaryCol],
                style));
 }
        internal DataGridViewSummaryRow(DataGridViewRow[] group, DataGridViewColumn groupFromCol, DataGridViewColumn groupToCol, SummaryRowPosition summaryPosition)
        {
            if (group == null)
            {
                throw new ArgumentNullException(nameof(group));
            }

            this.GroupRows       = group;
            this.GroupToColumn   = groupToCol;
            this.GroupFromColumn = groupFromCol;
            this.SummaryPosition = summaryPosition;
        }