예제 #1
0
파일: Table.cs 프로젝트: elea30/codereports
        public override void Render(Report report, Flow fm, DataContext dataContext)
        {
            HashSet<String> fieldNames = new HashSet<string>();

            foreach (var c in Columns)
            {
                if (c.DataField != "#")
                    fieldNames.Add(c.DataField);
                if (c.AggregateWeightDataField != null)
                    fieldNames.Add(c.AggregateWeightDataField);
            }

            foreach (var g in Groups)
                foreach (var sc in g.GroupByColumns)
                    fieldNames.Add(sc.DataField);

            var data = dataContext.CreateTable(DataTable, fieldNames.ToArray());

            for (int i = 0; i < Columns.Length; i++)
            {
                Columns[i]._Index = i;
                Columns[i]._DataFieldIndex = data.GetColumnIndex(Columns[i].DataField);

                if (Columns[i].AggregateWeightDataField != null)
                {
                    Columns[i]._AggregateWeightDataFieldIndex = data.GetColumnIndex(Columns[i].AggregateWeightDataField);
                    if (Columns[i]._AggregateWeightDataFieldIndex == -1)
                        throw new InvalidOperationException(String.Format("Weight column '{0}' not found.", Columns[i].AggregateWeightDataField));
                }
            }

            List<SortColumn> sort = new List<SortColumn>();
            List<GroupData> groupData = new List<GroupData>();

            int gi = 0;
            if (Groups != null)
            {
                foreach (var g in Groups)
                {
                    var gd = new GroupData();
                    var gc = new List<SortColumn>();
                    if (g.GroupByColumns!=null)
                        foreach (var c in g.GroupByColumns)
                        {
                            var ci = data.GetColumnIndex(c.DataField);
                            if (ci != -1)
                                gc.Add(new SortColumn
                                {
                                    ColumnIndex = ci,
                                    SortDirection = c.SortDirection == SortDirection.None ? SortDirection.Ascending : c.SortDirection
                                });
                        }
                    gd.Columns = gc.ToArray();
                    sort.AddRange(gd.Columns);
                    gd.GroupIndex = gi++;
                    gd.Group = g;

                    if (g.CaptionFormat != null)
                    {
                        String[] names;
                        String format;
                        StringFormatHelper.PrepareFormatWithNames(g.CaptionFormat, out format, out names);

                        gd.PreparedCaptionFormat = format;
                        gd.PreparedCaptionColumns = names != null ? names.Select(a => data.GetColumnIndex(a)).ToArray() : null;
                    }

                    if (g.FooterFormat != null)
                    {
                        String[] names;
                        String format;
                        StringFormatHelper.PrepareFormatWithNames(g.FooterFormat, out format, out names);

                        gd.PreparedFooterFormat = format;
                        gd.PreparedFooterColumns = names!=null ? names.Select(a => data.GetColumnIndex(a)).ToArray() : null;
                    }

                    gd.GroupLevel = Groups.Length - gd.GroupIndex - 1;
                    gd.GroupOpen = false;
                    groupData.Add(gd);
                }
            }

            sort.AddRange(from c in Columns
                          where c.SortDirection != SortDirection.None
                          orderby c.SortIndex
                          select new SortColumn
                          {
                              ColumnIndex = c._DataFieldIndex,
                              SortDirection = c.SortDirection
                          });

            if (sort.Count != 0)
                data.Sort(sort.ToArray());

            var rows = data.Rows;
            var rect = fm.GetRect(Position, Columns.Length, 0);
            var pos = new RowCol { Col = rect.Col1, Row = rect.Row1 };
            var startRow = pos.Row;
            List<Cell> cells = new List<Cell>();

            object[] accumulator = new object[Columns.Length];
            decimal[] count = new decimal[Columns.Length];
            CellAlignment[] align = new CellAlignment[Columns.Length];

            for (int c = 0; c < Columns.Length; c++)
            {
                var th = Columns[c]._DataFieldIndex >= 0 ? data.TypeHelper[Columns[c]._DataFieldIndex] : null;
                if (th != null)
                    switch (Columns[c].AggregateFunction)
                    {
                        case AggregateFunction.Sum:
                            accumulator[c] = th.Math.Zero;
                            break;
                        case AggregateFunction.Count:
                            accumulator[c] = 0;
                            break;
                        case AggregateFunction.Avg:
                            if (Columns[c].AggregateWeightDataField != null)
                            {
                                count[c] = 0;
                                accumulator[c] = 0m;
                            }
                            else
                            {
                                accumulator[c] = th.Math.Zero;
                                count[c] = 0;
                            }
                            break;
                        case AggregateFunction.Product:
                            accumulator[c] = th.Math.One;
                            break;
                    }
                align[c] = th != null ? CalcAlignment(Columns[c].CellAlignment, th) : CellAlignment.Right;
            }

            Data.Row prevRow = null;
            for (int r = 0, rowIndex = 0; r <= rows.Length; r++, rowIndex++)
            {
                var row = r < rows.Length ? rows[r] : null;
                var closeToGroupLevel = row!=null ? groupData.Count : 0;

                if (prevRow != null && row != null)
                    for (int g = 0; g < groupData.Count; g++)
                        if (RowComparer.Compare(row, prevRow, groupData[g].Columns) != 0)
                        {
                            closeToGroupLevel = g;
                            break;
                        }

                //close higher groups first
                for (int g = groupData.Count - 1; g >= closeToGroupLevel; g--)
                {
                    var gd = groupData[g];
                    if (gd.GroupOpen)
                    {
                        gd.GroupOpen = false;
                        //close group
                        if (gd.Group.ShowFooter)
                        {
                            for (int c = 0; c < Columns.Length; c++)
                            {
                                if (Columns[c].AggregateFunction == AggregateFunction.Avg)
                                    gd.GroupAccumulator[c] = CalculateAggregate(Columns[c].AggregateFunction, gd.GroupAccumulator[c], gd.GroupCounter[c]);

                                var style = gd.GetFooterCellStyle(Columns[c].ColumnType);
                                switch (Columns[c].FooterType)
                                {
                                    case ColumnFooterType.FooterText:
                                        cells.Add(new Cell { Column = pos.Col + c, Row = pos.Row, FormattedValue = Columns[c].FooterText, Value = Columns[c].FooterText, CellStyleIndex = style, Alignment = Columns[c].FooterAlignment });
                                        break;
                                    case ColumnFooterType.AggregateValue:
                                        String fv = (Columns[c].FooterFormat != null) ? String.Format(Columns[c].FooterFormat, gd.GroupAccumulator[c]) : (gd.GroupAccumulator[c] != null ? gd.GroupAccumulator[c].ToString() : null);
                                        var al = Columns[c]._DataFieldIndex >= 0 ? CalcAlignment(Columns[c].FooterAlignment, data.TypeHelper[Columns[c]._DataFieldIndex]) : CellAlignment.Auto;
                                        cells.Add(new Cell { Column = pos.Col + c, Row = pos.Row, FormattedValue = fv, Value = gd.GroupAccumulator[c], CellStyleIndex = style, Alignment = al, Format = Columns[c].FooterFormat });
                                        break;
                                    case ColumnFooterType.GroupFooter:
                                        String gfv = gd.PreparedFooterColumns == null ? gd.PreparedFooterFormat : String.Format(gd.PreparedFooterFormat, prevRow.GetMany(gd.PreparedFooterColumns));
                                        var gal = Columns[c]._DataFieldIndex >= 0 ? CalcAlignment(Columns[c].FooterAlignment, data.TypeHelper[Columns[c]._DataFieldIndex]) : CellAlignment.Auto;
                                        cells.Add(new Cell { Column = pos.Col + c, Row = pos.Row, FormattedValue = gfv, Value = gfv, CellStyleIndex = style, Alignment = gal });
                                        break;
                                }
                                if (Columns[c].FooterColSpan > 1)
                                {
                                    report.MergedCells.Add(new Rect { Col1 = pos.Col + c, Col2 = pos.Col + c + Columns[c].FooterColSpan - 1, Row1 = pos.Row, Row2 = pos.Row });
                                    c += Columns[c].FooterColSpan - 1;
                                }
                            }
                            pos.Row++;
                        }
                    }
                }

                for (int g = 0; g < groupData.Count; g++) {
                    var gd = groupData[g];
                    //add row
                    if (row != null)
                    {
                        if (!gd.GroupOpen)
                        {
                            gd.GroupOpen = true;
                            rowIndex = 0;

                            if (gd.Group.ShowCaption)
                            {
                                String caption = gd.PreparedCaptionColumns == null ? gd.PreparedCaptionFormat : String.Format(gd.PreparedCaptionFormat, row.GetMany(gd.PreparedCaptionColumns));

                                cells.Add(new Cell { Column = pos.Col, Row = pos.Row, FormattedValue = caption, Value = caption, CellStyleIndex = gd.GetCaptionStyle()});
                                report.MergedCells.Add(new Rect { Col1 = pos.Col, Col2 = pos.Col + Columns.Length - 1, Row1 = pos.Row, Row2 = pos.Row });
                                pos.Row++;
                            }

                            if (gd.Group.ShowHeader)
                            {
                                for (int c = 0; c < Columns.Length; c++)
                                {
                                    var ht = Columns[c].HeaderText;
                                    var style = gd.GetHeaderCellStyle(Columns[c].ColumnType);
                                    var a = Columns[c]._DataFieldIndex >= 0 ? CalcAlignment(Columns[c].HeaderAlignment, data.TypeHelper[Columns[c]._DataFieldIndex]) : CellAlignment.Right;

                                    cells.Add(new Cell
                                    {
                                        Column = pos.Col + c,
                                        Row = pos.Row,
                                        FormattedValue = ht,
                                        Value = ht,
                                        CellStyleIndex = style,
                                        Alignment = a
                                    });
                                }
                                pos.Row++;
                            }

                            gd.GroupAccumulator = new object[Columns.Length];
                            gd.GroupCounter = new decimal[Columns.Length];

                            //reset group accumulator
                            for (int c = 0; c < Columns.Length; c++)
                            {
                                var th = Columns[c]._DataFieldIndex >= 0 ? data.TypeHelper[Columns[c]._DataFieldIndex] : null;
                                if (th != null)
                                    switch (Columns[c].AggregateFunction)
                                    {
                                        case AggregateFunction.Sum:
                                            gd.GroupAccumulator[c] = th.Math.Zero;
                                            break;
                                        case AggregateFunction.Count:
                                            gd.GroupAccumulator[c] = 0;
                                            break;
                                        case AggregateFunction.Avg:
                                            if (Columns[c].AggregateWeightDataField != null)
                                            {
                                                gd.GroupAccumulator[c] = 0m;
                                                gd.GroupCounter[c] = 0;
                                            }
                                            else
                                            {
                                                gd.GroupAccumulator[c] = th.Math.Zero;
                                                gd.GroupCounter[c] = 0;
                                            }
                                            break;
                                        case AggregateFunction.Product:
                                            gd.GroupAccumulator[c] = th.Math.One;
                                            break;
                                    }
                            }
                        }

                        for (int c = 0; c < Columns.Length; c++)
                        {
                            var dfi = Columns[c]._DataFieldIndex;
                            var v = row[dfi];

                            var th = dfi >= 0 ? data.TypeHelper[dfi] : null;
                            if (th != null)
                            {
                                switch (Columns[c].AggregateFunction)
                                {
                                    case AggregateFunction.Sum:
                                        gd.GroupAccumulator[c] = th.Math.SumNullAsZero(gd.GroupAccumulator[c], v);
                                        break;
                                    case AggregateFunction.Count:
                                        if (v != null)
                                            gd.GroupAccumulator[c] = (int)gd.GroupAccumulator[c] + 1;
                                        break;
                                    case AggregateFunction.Product:
                                        gd.GroupAccumulator[c] = th.Math.Multiply(gd.GroupAccumulator[c], v);
                                        break;
                                    case AggregateFunction.Avg:
                                        if (v != null)
                                        {
                                            if (Columns[c].AggregateWeightDataField != null)
                                            {
                                                var w = row[Columns[c]._AggregateWeightDataFieldIndex];
                                                if (w != null)
                                                {
                                                    var wd = Convert.ToDecimal(w);
                                                    var wv = wd * Convert.ToDecimal(v);
                                                    gd.GroupAccumulator[c] = (decimal)gd.GroupAccumulator[c] + wv;
                                                    gd.GroupCounter[c] += wd;
                                                }
                                            }
                                            else
                                            {
                                                gd.GroupAccumulator[c] = th.Math.Sum(gd.GroupAccumulator[c], v);
                                                ++gd.GroupCounter[c];
                                            }
                                        }
                                        break;
                                    case AggregateFunction.Min:
                                        if (gd.GroupAccumulator[c] == null)
                                            gd.GroupAccumulator[c] = v;
                                        else if (v != null)
                                            gd.GroupAccumulator[c] = th.Math.Min(gd.GroupAccumulator[c], v);
                                        break;
                                    case AggregateFunction.Max:
                                        if (gd.GroupAccumulator[c] == null)
                                            gd.GroupAccumulator[c] = v;
                                        else if (v != null)
                                            gd.GroupAccumulator[c] = th.Math.Max(gd.GroupAccumulator[c], v);
                                        break;
                                }
                            }

                            switch (Columns[c].CellDisplayMode)
                            {
                                default:
                                case CellDisplayMode.Normal:
                                    v = Columns[c].DataField == "#" ? rowIndex + 1 : row[dfi];
                                    break;
                                case CellDisplayMode.RowNumber:
                                    v = rowIndex + 1;
                                    break;
                                case CellDisplayMode.AccumulatorValue:
                                    v = CalculateAggregate(Columns[c].AggregateFunction, gd.GroupAccumulator[c], gd.GroupCounter[c]);
                                    break;
                            }

                            CellStyle addStyle = null;
                            if (Columns[c].ConditionalFormatting != null)
                                addStyle = Columns[c].ConditionalFormatting(v);

                            String fv = (Columns[c].Format != null) ? String.Format(Columns[c].Format, v) : (v != null ? v.ToString() : null);

                            if (g + 1 == groupData.Count)
                                cells.Add(new Cell
                                {
                                    Column = pos.Col + c,
                                    Row = pos.Row,
                                    Value = v,
                                    FormattedValue = fv,
                                    CellStyleIndex = Columns[c].ColumnType == TableColumnType.HeaderColumn ? CellStyleIndex.TableRowHeader : Columns[c].ColumnType == TableColumnType.FooterColumn ? CellStyleIndex.TableRowFooter : CellStyleIndex.TableRow,
                                    Alignment = align[c],
                                    Format = Columns[c].Format,
                                    CustomStyle = addStyle
                                });
                        }

                        if (g + 1 == groupData.Count)
                            pos.Row++;
                    }
                }

                prevRow = row;
            }

            fm.GetRect(RowCol.Zero, 0, pos.Row - startRow);

            report.Cells.AddRange(cells);
        }
예제 #2
0
파일: Flow.cs 프로젝트: elea30/codereports
        public Rect GetRect(RowCol pos, int w, int h)
        {
            if (pos == null)
            {
                throw new ArgumentNullException("Flow.GetRect: Argument 'pos' is null!");
            }

            Rect res = new Rect();

            if (pos.Col < 0)
            {
                res.Col1 = bounds.Col2 + pos.Col - w + 1;
                res.Col2 = res.Col1 + w - 1;
                if (Orientation == FlowOrientation.Horizontal)
                {
                    bounds.Col2 = res.Col1 - 1;
                }
                else
                if (res.Col1 > bounds.Col1)
                {
                    bounds.Col1 = res.Col1;
                }
            }
            else
            {
                res.Col1 = bounds.Col1 + pos.Col;
                res.Col2 = res.Col1 + w - 1;
                if (Orientation == FlowOrientation.Horizontal)
                {
                    bounds.Col1 = res.Col2 + 1;
                }
                else
                if (res.Col2 > bounds.Col2)
                {
                    bounds.Col2 = res.Col2;
                }
            }

            if (pos.Row < 0)
            {
                res.Row1 = bounds.Row2 + pos.Row - h + 1;
                res.Row2 = res.Row1 + h - 1;
                if (Orientation == FlowOrientation.Vertical)
                {
                    bounds.Row2 = res.Row1 - 1;
                }
                else
                if (res.Row1 > bounds.Row1)
                {
                    bounds.Row1 = res.Row1;
                }
            }
            else
            {
                res.Row1 = bounds.Row1 + pos.Row;
                res.Row2 = res.Row1 + h - 1;
                if (Orientation == FlowOrientation.Vertical)
                {
                    bounds.Row1 = res.Row2 + 1;
                }
                else
                if (res.Row2 > bounds.Row2)
                {
                    bounds.Row2 = res.Row2;
                }
            }
            return(res);
        }
예제 #3
0
파일: Table.cs 프로젝트: elea30/codereports
        public override void Render(Report report, Flow fm, DataContext dataContext)
        {
            HashSet <String> fieldNames = new HashSet <string>();

            foreach (var c in Columns)
            {
                if (c.DataField != "#")
                {
                    fieldNames.Add(c.DataField);
                }
                if (c.AggregateWeightDataField != null)
                {
                    fieldNames.Add(c.AggregateWeightDataField);
                }
            }

            foreach (var g in Groups)
            {
                foreach (var sc in g.GroupByColumns)
                {
                    fieldNames.Add(sc.DataField);
                }
            }

            var data = dataContext.CreateTable(DataTable, fieldNames.ToArray());


            for (int i = 0; i < Columns.Length; i++)
            {
                Columns[i]._Index          = i;
                Columns[i]._DataFieldIndex = data.GetColumnIndex(Columns[i].DataField);

                if (Columns[i].AggregateWeightDataField != null)
                {
                    Columns[i]._AggregateWeightDataFieldIndex = data.GetColumnIndex(Columns[i].AggregateWeightDataField);
                    if (Columns[i]._AggregateWeightDataFieldIndex == -1)
                    {
                        throw new InvalidOperationException(String.Format("Weight column '{0}' not found.", Columns[i].AggregateWeightDataField));
                    }
                }
            }



            List <SortColumn> sort      = new List <SortColumn>();
            List <GroupData>  groupData = new List <GroupData>();

            int gi = 0;

            if (Groups != null)
            {
                foreach (var g in Groups)
                {
                    var gd = new GroupData();
                    var gc = new List <SortColumn>();
                    if (g.GroupByColumns != null)
                    {
                        foreach (var c in g.GroupByColumns)
                        {
                            var ci = data.GetColumnIndex(c.DataField);
                            if (ci != -1)
                            {
                                gc.Add(new SortColumn
                                {
                                    ColumnIndex   = ci,
                                    SortDirection = c.SortDirection == SortDirection.None ? SortDirection.Ascending : c.SortDirection
                                });
                            }
                        }
                    }
                    gd.Columns = gc.ToArray();
                    sort.AddRange(gd.Columns);
                    gd.GroupIndex = gi++;
                    gd.Group      = g;

                    if (g.CaptionFormat != null)
                    {
                        String[] names;
                        String   format;
                        StringFormatHelper.PrepareFormatWithNames(g.CaptionFormat, out format, out names);

                        gd.PreparedCaptionFormat  = format;
                        gd.PreparedCaptionColumns = names != null?names.Select(a => data.GetColumnIndex(a)).ToArray() : null;
                    }

                    if (g.FooterFormat != null)
                    {
                        String[] names;
                        String   format;
                        StringFormatHelper.PrepareFormatWithNames(g.FooterFormat, out format, out names);

                        gd.PreparedFooterFormat  = format;
                        gd.PreparedFooterColumns = names != null?names.Select(a => data.GetColumnIndex(a)).ToArray() : null;
                    }

                    gd.GroupLevel = Groups.Length - gd.GroupIndex - 1;
                    gd.GroupOpen  = false;
                    groupData.Add(gd);
                }
            }

            sort.AddRange(from c in Columns
                          where c.SortDirection != SortDirection.None
                          orderby c.SortIndex
                          select new SortColumn
            {
                ColumnIndex   = c._DataFieldIndex,
                SortDirection = c.SortDirection
            });

            if (sort.Count != 0)
            {
                data.Sort(sort.ToArray());
            }

            var rows = data.Rows;
            var rect = fm.GetRect(Position, Columns.Length, 0);
            var pos  = new RowCol {
                Col = rect.Col1, Row = rect.Row1
            };
            var         startRow = pos.Row;
            List <Cell> cells    = new List <Cell>();


            object[]        accumulator = new object[Columns.Length];
            decimal[]       count       = new decimal[Columns.Length];
            CellAlignment[] align       = new CellAlignment[Columns.Length];

            for (int c = 0; c < Columns.Length; c++)
            {
                var th = Columns[c]._DataFieldIndex >= 0 ? data.TypeHelper[Columns[c]._DataFieldIndex] : null;
                if (th != null)
                {
                    switch (Columns[c].AggregateFunction)
                    {
                    case AggregateFunction.Sum:
                        accumulator[c] = th.Math.Zero;
                        break;

                    case AggregateFunction.Count:
                        accumulator[c] = 0;
                        break;

                    case AggregateFunction.Avg:
                        if (Columns[c].AggregateWeightDataField != null)
                        {
                            count[c]       = 0;
                            accumulator[c] = 0m;
                        }
                        else
                        {
                            accumulator[c] = th.Math.Zero;
                            count[c]       = 0;
                        }
                        break;

                    case AggregateFunction.Product:
                        accumulator[c] = th.Math.One;
                        break;
                    }
                }
                align[c] = th != null?CalcAlignment(Columns[c].CellAlignment, th) : CellAlignment.Right;
            }

            Data.Row prevRow = null;
            for (int r = 0, rowIndex = 0; r <= rows.Length; r++, rowIndex++)
            {
                var row = r < rows.Length ? rows[r] : null;
                var closeToGroupLevel = row != null ? groupData.Count : 0;

                if (prevRow != null && row != null)
                {
                    for (int g = 0; g < groupData.Count; g++)
                    {
                        if (RowComparer.Compare(row, prevRow, groupData[g].Columns) != 0)
                        {
                            closeToGroupLevel = g;
                            break;
                        }
                    }
                }

                //close higher groups first
                for (int g = groupData.Count - 1; g >= closeToGroupLevel; g--)
                {
                    var gd = groupData[g];
                    if (gd.GroupOpen)
                    {
                        gd.GroupOpen = false;
                        //close group
                        if (gd.Group.ShowFooter)
                        {
                            for (int c = 0; c < Columns.Length; c++)
                            {
                                if (Columns[c].AggregateFunction == AggregateFunction.Avg)
                                {
                                    gd.GroupAccumulator[c] = CalculateAggregate(Columns[c].AggregateFunction, gd.GroupAccumulator[c], gd.GroupCounter[c]);
                                }

                                var style = gd.GetFooterCellStyle(Columns[c].ColumnType);
                                switch (Columns[c].FooterType)
                                {
                                case ColumnFooterType.FooterText:
                                    cells.Add(new Cell {
                                        Column = pos.Col + c, Row = pos.Row, FormattedValue = Columns[c].FooterText, Value = Columns[c].FooterText, CellStyleIndex = style, Alignment = Columns[c].FooterAlignment
                                    });
                                    break;

                                case ColumnFooterType.AggregateValue:
                                    String fv = (Columns[c].FooterFormat != null) ? String.Format(Columns[c].FooterFormat, gd.GroupAccumulator[c]) : (gd.GroupAccumulator[c] != null ? gd.GroupAccumulator[c].ToString() : null);
                                    var    al = Columns[c]._DataFieldIndex >= 0 ? CalcAlignment(Columns[c].FooterAlignment, data.TypeHelper[Columns[c]._DataFieldIndex]) : CellAlignment.Auto;
                                    cells.Add(new Cell {
                                        Column = pos.Col + c, Row = pos.Row, FormattedValue = fv, Value = gd.GroupAccumulator[c], CellStyleIndex = style, Alignment = al, Format = Columns[c].FooterFormat
                                    });
                                    break;

                                case ColumnFooterType.GroupFooter:
                                    String gfv = gd.PreparedFooterColumns == null ? gd.PreparedFooterFormat : String.Format(gd.PreparedFooterFormat, prevRow.GetMany(gd.PreparedFooterColumns));
                                    var    gal = Columns[c]._DataFieldIndex >= 0 ? CalcAlignment(Columns[c].FooterAlignment, data.TypeHelper[Columns[c]._DataFieldIndex]) : CellAlignment.Auto;
                                    cells.Add(new Cell {
                                        Column = pos.Col + c, Row = pos.Row, FormattedValue = gfv, Value = gfv, CellStyleIndex = style, Alignment = gal
                                    });
                                    break;
                                }
                                if (Columns[c].FooterColSpan > 1)
                                {
                                    report.MergedCells.Add(new Rect {
                                        Col1 = pos.Col + c, Col2 = pos.Col + c + Columns[c].FooterColSpan - 1, Row1 = pos.Row, Row2 = pos.Row
                                    });
                                    c += Columns[c].FooterColSpan - 1;
                                }
                            }
                            pos.Row++;
                        }
                    }
                }

                for (int g = 0; g < groupData.Count; g++)
                {
                    var gd = groupData[g];
                    //add row
                    if (row != null)
                    {
                        if (!gd.GroupOpen)
                        {
                            gd.GroupOpen = true;
                            rowIndex     = 0;

                            if (gd.Group.ShowCaption)
                            {
                                String caption = gd.PreparedCaptionColumns == null ? gd.PreparedCaptionFormat : String.Format(gd.PreparedCaptionFormat, row.GetMany(gd.PreparedCaptionColumns));

                                cells.Add(new Cell {
                                    Column = pos.Col, Row = pos.Row, FormattedValue = caption, Value = caption, CellStyleIndex = gd.GetCaptionStyle()
                                });
                                report.MergedCells.Add(new Rect {
                                    Col1 = pos.Col, Col2 = pos.Col + Columns.Length - 1, Row1 = pos.Row, Row2 = pos.Row
                                });
                                pos.Row++;
                            }

                            if (gd.Group.ShowHeader)
                            {
                                for (int c = 0; c < Columns.Length; c++)
                                {
                                    var ht    = Columns[c].HeaderText;
                                    var style = gd.GetHeaderCellStyle(Columns[c].ColumnType);
                                    var a     = Columns[c]._DataFieldIndex >= 0 ? CalcAlignment(Columns[c].HeaderAlignment, data.TypeHelper[Columns[c]._DataFieldIndex]) : CellAlignment.Right;

                                    cells.Add(new Cell
                                    {
                                        Column         = pos.Col + c,
                                        Row            = pos.Row,
                                        FormattedValue = ht,
                                        Value          = ht,
                                        CellStyleIndex = style,
                                        Alignment      = a
                                    });
                                }
                                pos.Row++;
                            }

                            gd.GroupAccumulator = new object[Columns.Length];
                            gd.GroupCounter     = new decimal[Columns.Length];

                            //reset group accumulator
                            for (int c = 0; c < Columns.Length; c++)
                            {
                                var th = Columns[c]._DataFieldIndex >= 0 ? data.TypeHelper[Columns[c]._DataFieldIndex] : null;
                                if (th != null)
                                {
                                    switch (Columns[c].AggregateFunction)
                                    {
                                    case AggregateFunction.Sum:
                                        gd.GroupAccumulator[c] = th.Math.Zero;
                                        break;

                                    case AggregateFunction.Count:
                                        gd.GroupAccumulator[c] = 0;
                                        break;

                                    case AggregateFunction.Avg:
                                        if (Columns[c].AggregateWeightDataField != null)
                                        {
                                            gd.GroupAccumulator[c] = 0m;
                                            gd.GroupCounter[c]     = 0;
                                        }
                                        else
                                        {
                                            gd.GroupAccumulator[c] = th.Math.Zero;
                                            gd.GroupCounter[c]     = 0;
                                        }
                                        break;

                                    case AggregateFunction.Product:
                                        gd.GroupAccumulator[c] = th.Math.One;
                                        break;
                                    }
                                }
                            }
                        }

                        for (int c = 0; c < Columns.Length; c++)
                        {
                            var dfi = Columns[c]._DataFieldIndex;
                            var v   = row[dfi];

                            var th = dfi >= 0 ? data.TypeHelper[dfi] : null;
                            if (th != null)
                            {
                                switch (Columns[c].AggregateFunction)
                                {
                                case AggregateFunction.Sum:
                                    gd.GroupAccumulator[c] = th.Math.SumNullAsZero(gd.GroupAccumulator[c], v);
                                    break;

                                case AggregateFunction.Count:
                                    if (v != null)
                                    {
                                        gd.GroupAccumulator[c] = (int)gd.GroupAccumulator[c] + 1;
                                    }
                                    break;

                                case AggregateFunction.Product:
                                    gd.GroupAccumulator[c] = th.Math.Multiply(gd.GroupAccumulator[c], v);
                                    break;

                                case AggregateFunction.Avg:
                                    if (v != null)
                                    {
                                        if (Columns[c].AggregateWeightDataField != null)
                                        {
                                            var w = row[Columns[c]._AggregateWeightDataFieldIndex];
                                            if (w != null)
                                            {
                                                var wd = Convert.ToDecimal(w);
                                                var wv = wd * Convert.ToDecimal(v);
                                                gd.GroupAccumulator[c] = (decimal)gd.GroupAccumulator[c] + wv;
                                                gd.GroupCounter[c]    += wd;
                                            }
                                        }
                                        else
                                        {
                                            gd.GroupAccumulator[c] = th.Math.Sum(gd.GroupAccumulator[c], v);
                                            ++gd.GroupCounter[c];
                                        }
                                    }
                                    break;

                                case AggregateFunction.Min:
                                    if (gd.GroupAccumulator[c] == null)
                                    {
                                        gd.GroupAccumulator[c] = v;
                                    }
                                    else if (v != null)
                                    {
                                        gd.GroupAccumulator[c] = th.Math.Min(gd.GroupAccumulator[c], v);
                                    }
                                    break;

                                case AggregateFunction.Max:
                                    if (gd.GroupAccumulator[c] == null)
                                    {
                                        gd.GroupAccumulator[c] = v;
                                    }
                                    else if (v != null)
                                    {
                                        gd.GroupAccumulator[c] = th.Math.Max(gd.GroupAccumulator[c], v);
                                    }
                                    break;
                                }
                            }

                            switch (Columns[c].CellDisplayMode)
                            {
                            default:
                            case CellDisplayMode.Normal:
                                v = Columns[c].DataField == "#" ? rowIndex + 1 : row[dfi];
                                break;

                            case CellDisplayMode.RowNumber:
                                v = rowIndex + 1;
                                break;

                            case CellDisplayMode.AccumulatorValue:
                                v = CalculateAggregate(Columns[c].AggregateFunction, gd.GroupAccumulator[c], gd.GroupCounter[c]);
                                break;
                            }

                            CellStyle addStyle = null;
                            if (Columns[c].ConditionalFormatting != null)
                            {
                                addStyle = Columns[c].ConditionalFormatting(v);
                            }

                            String fv = (Columns[c].Format != null) ? String.Format(Columns[c].Format, v) : (v != null ? v.ToString() : null);

                            if (g + 1 == groupData.Count)
                            {
                                cells.Add(new Cell
                                {
                                    Column         = pos.Col + c,
                                    Row            = pos.Row,
                                    Value          = v,
                                    FormattedValue = fv,
                                    CellStyleIndex = Columns[c].ColumnType == TableColumnType.HeaderColumn ? CellStyleIndex.TableRowHeader : Columns[c].ColumnType == TableColumnType.FooterColumn ? CellStyleIndex.TableRowFooter : CellStyleIndex.TableRow,
                                    Alignment      = align[c],
                                    Format         = Columns[c].Format,
                                    CustomStyle    = addStyle
                                });
                            }
                        }

                        if (g + 1 == groupData.Count)
                        {
                            pos.Row++;
                        }
                    }
                }

                prevRow = row;
            }

            fm.GetRect(RowCol.Zero, 0, pos.Row - startRow);

            report.Cells.AddRange(cells);
        }