using System; using System.Collections.Generic; using System.Text; using FastReport.Data; using FastReport.Table; using FastReport.Utils; using System.Drawing; using System.Collections; namespace FastReport.Matrix { internal class MatrixHelper { private MatrixObject matrix; private int headerWidth; private int headerHeight; private int bodyWidth; private int bodyHeight; private bool designTime; private TableResult resultTable; private MatrixDescriptor titleDescriptor; private MatrixDescriptor cellHeaderDescriptor; private MatrixHeaderDescriptor noColumnsDescriptor; private MatrixHeaderDescriptor noRowsDescriptor; private MatrixCellDescriptor noCellsDescriptor; private object[] cellValues; private int[] evenStyleIndices; private bool noColumns; private bool noRows; #region Properties public MatrixObject Matrix { get { return matrix; } } public Report Report { get { return Matrix.Report; } } public TableResult ResultTable { get { return designTime ? resultTable : Matrix.ResultTable; } } public int HeaderHeight { get { return headerHeight; } } public int HeaderWidth { get { return headerWidth; } } public int BodyWidth { get { return bodyWidth; } } public int BodyHeight { get { return bodyHeight; } } public object[] CellValues { get { return cellValues; } } #endregion #region Private Methods private string ExtractColumnName(string complexName) { if (complexName.StartsWith("[") && complexName.EndsWith("]")) complexName = complexName.Substring(1, complexName.Length - 2); if (Report == null) return complexName; Column column = DataHelper.GetColumn(Report.Dictionary, complexName); if (column == null) return complexName; return column.Alias; } /// /// Updates HeaderWidth, HeaderHeight, BodyWidth, BodyHeight properties. /// private void UpdateTemplateSizes() { headerWidth = Matrix.Data.Rows.Count; if (headerWidth == 0) headerWidth = 1; if (Matrix.Data.Cells.Count > 1 && !Matrix.CellsSideBySide) headerWidth++; headerHeight = Matrix.Data.Columns.Count; if (headerHeight == 0) headerHeight = 1; if (Matrix.Data.Cells.Count > 1 && Matrix.CellsSideBySide) headerHeight++; if (Matrix.ShowTitle) headerHeight++; bodyWidth = 1; foreach (MatrixHeaderDescriptor descr in Matrix.Data.Columns) { if (descr.Totals) bodyWidth++; } if (Matrix.CellsSideBySide && Matrix.Data.Cells.Count > 1) bodyWidth *= Matrix.Data.Cells.Count; bodyHeight = 1; foreach (MatrixHeaderDescriptor descr in Matrix.Data.Rows) { if (descr.Totals) bodyHeight++; } if (!Matrix.CellsSideBySide && Matrix.Data.Cells.Count > 1) bodyHeight *= Matrix.Data.Cells.Count; } private void UpdateColumnDescriptors() { int left = HeaderWidth; int top = 0; int width = BodyWidth; int dataWidth = 1; if (Matrix.Data.Cells.Count > 1 && Matrix.CellsSideBySide) dataWidth = Matrix.Data.Cells.Count; if (Matrix.ShowTitle) top++; foreach (MatrixHeaderDescriptor descr in Matrix.Data.Columns) { if (descr.Totals) { descr.TemplateTotalColumn = Matrix.Columns[left + width - dataWidth]; descr.TemplateTotalRow = Matrix.Rows[top]; descr.TemplateTotalCell = Matrix[left + width - dataWidth, top]; width -= dataWidth; } else { descr.TemplateTotalColumn = null; descr.TemplateTotalRow = null; descr.TemplateTotalCell = null; } descr.TemplateColumn = Matrix.Columns[left]; descr.TemplateRow = Matrix.Rows[top]; descr.TemplateCell = Matrix[left, top]; top++; } } private void UpdateRowDescriptors() { int left = 0; int top = HeaderHeight; int height = BodyHeight; int dataHeight = 1; if (Matrix.Data.Cells.Count > 1 && !Matrix.CellsSideBySide) dataHeight = Matrix.Data.Cells.Count; foreach (MatrixHeaderDescriptor descr in Matrix.Data.Rows) { if (descr.Totals) { descr.TemplateTotalColumn = Matrix.Columns[left]; descr.TemplateTotalRow = Matrix.Rows[top + height - dataHeight]; descr.TemplateTotalCell = Matrix[left, top + height - dataHeight]; height -= dataHeight; } else { descr.TemplateTotalColumn = null; descr.TemplateTotalRow = null; descr.TemplateTotalCell = null; } descr.TemplateColumn = Matrix.Columns[left]; descr.TemplateRow = Matrix.Rows[top]; descr.TemplateCell = Matrix[left, top]; left++; } } private void UpdateCellDescriptors() { int x = HeaderWidth; int y = HeaderHeight; foreach (MatrixCellDescriptor descr in Matrix.Data.Cells) { descr.TemplateColumn = Matrix.Columns[x]; descr.TemplateRow = Matrix.Rows[y]; descr.TemplateCell = Matrix[x, y]; if (Matrix.CellsSideBySide) x++; else y++; } } private void UpdateOtherDescriptors() { titleDescriptor.TemplateColumn = null; titleDescriptor.TemplateRow = null; titleDescriptor.TemplateCell = null; if (Matrix.ShowTitle) { titleDescriptor.TemplateColumn = Matrix.Columns[HeaderWidth]; titleDescriptor.TemplateRow = Matrix.Rows[0]; titleDescriptor.TemplateCell = Matrix[HeaderWidth, 0]; } cellHeaderDescriptor.TemplateColumn = null; cellHeaderDescriptor.TemplateRow = null; cellHeaderDescriptor.TemplateCell = null; if (Matrix.Data.Cells.Count > 1) { if (Matrix.CellsSideBySide) { cellHeaderDescriptor.TemplateColumn = Matrix.Columns[HeaderWidth]; cellHeaderDescriptor.TemplateRow = Matrix.Rows[HeaderHeight - 1]; } else { cellHeaderDescriptor.TemplateColumn = Matrix.Columns[HeaderWidth - 1]; cellHeaderDescriptor.TemplateRow = Matrix.Rows[HeaderHeight]; } } } private void ApplyStyle(TableCell cell, string styleName) { Style style = null; int styleIndex = Matrix.StyleSheet.IndexOf(Matrix.Style); if (styleIndex != -1) { StyleCollection styles = Matrix.StyleSheet[styleIndex]; style = styles[styles.IndexOf(styleName)]; } if (style != null) cell.ApplyStyle(style); } private TableCell CreateCell(string text) { TableCell cell = new TableCell(); cell.Font = DrawUtils.DefaultReportFont; cell.Text = text; cell.HorzAlign = HorzAlign.Center; cell.VertAlign = VertAlign.Center; ApplyStyle(cell, "Header"); return cell; } private TableCell CreateDataCell() { TableCell cell = new TableCell(); cell.Font = DrawUtils.DefaultReportFont; cell.HorzAlign = HorzAlign.Right; cell.VertAlign = VertAlign.Center; ApplyStyle(cell, "Body"); return cell; } private void SetHint(TableCell cell, string text) { cell.Assign(Matrix.Styles.DefaultStyle); cell.Text = text; cell.Font = DrawUtils.DefaultReportFont; cell.TextFill = new SolidFill(Color.Gray); cell.HorzAlign = HorzAlign.Center; cell.VertAlign = VertAlign.Center; cell.SetFlags(Flags.CanEdit, false); } private Point GetBodyLocation() { // determine the template's body location. Do not rely on HeaderWidth, HeaderHeight - // the template may be empty Point result = new Point(); foreach (MatrixHeaderDescriptor descr in Matrix.Data.Columns) { if (descr.TemplateColumn != null) result.X = descr.TemplateColumn.Index; } foreach (MatrixHeaderDescriptor descr in Matrix.Data.Rows) { if (descr.TemplateRow != null) result.Y = descr.TemplateRow.Index; } return result; } private void InitResultTable(bool isTemplate) { Matrix.Data.Columns.AddTotalItems(isTemplate); Matrix.Data.Rows.AddTotalItems(isTemplate); List columnTerminalItems = Matrix.Data.Columns.RootItem.GetTerminalItems(); List rowTerminalItems = Matrix.Data.Rows.RootItem.GetTerminalItems(); // create corner List descrList = new List(); if (Matrix.ShowTitle) descrList.Add(titleDescriptor); descrList.AddRange(Matrix.Data.Columns.ToArray()); if (Matrix.Data.Cells.Count > 1 && Matrix.CellsSideBySide) descrList.Add(cellHeaderDescriptor); foreach (MatrixDescriptor descr in descrList) { TableRow row = new TableRow(); if (descr.TemplateRow != null) row.Assign(descr.TemplateRow); ResultTable.Rows.Add(row); } descrList.Clear(); descrList.AddRange(Matrix.Data.Rows.ToArray()); if (Matrix.Data.Cells.Count > 1 && !Matrix.CellsSideBySide) descrList.Add(cellHeaderDescriptor); foreach (MatrixDescriptor descr in descrList) { TableColumn column = new TableColumn(); if (descr.TemplateColumn != null) column.Assign(descr.TemplateColumn); ResultTable.Columns.Add(column); } // determine the body location Point bodyLocation = GetBodyLocation(); // create columns foreach (MatrixHeaderItem item in columnTerminalItems) { foreach (MatrixCellDescriptor descr in Matrix.Data.Cells) { TableColumn column = new TableColumn(); if (item.TemplateColumn != null && descr.TemplateColumn != null) column.Assign(Matrix.Columns[item.TemplateColumn.Index + (descr.TemplateColumn.Index - bodyLocation.X)]); ResultTable.Columns.Add(column); if (!Matrix.CellsSideBySide) break; } } // create rows foreach (MatrixHeaderItem item in rowTerminalItems) { foreach (MatrixCellDescriptor descr in Matrix.Data.Cells) { TableRow row = new TableRow(); if (item.TemplateRow != null && descr.TemplateRow != null) row.Assign(Matrix.Rows[item.TemplateRow.Index + (descr.TemplateRow.Index - bodyLocation.Y)]); ResultTable.Rows.Add(row); if (Matrix.CellsSideBySide) break; } } } private void PrintHeaderCell(MatrixHeaderItem item, TableCellData resultCell, bool isEven) { TableCell templateCell = item.TemplateCell; if (templateCell != null) { if (designTime) { if (!item.IsTotal) templateCell.Text = item.Value.ToString(); resultCell.RunTimeAssign(templateCell, true); } else { if (Matrix.DataSource != null) Matrix.DataSource.CurrentRowNo = item.DataRowNo; templateCell.SetValue(item.Value); if (!item.IsTotal) templateCell.Text = templateCell.Format.FormatValue(item.Value); templateCell.SaveState(); if (isEven) templateCell.ApplyEvenStyle(); templateCell.GetData(); if (String.IsNullOrEmpty(templateCell.Hyperlink.Expression) && (templateCell.Hyperlink.Kind == HyperlinkKind.DetailReport || templateCell.Hyperlink.Kind == HyperlinkKind.DetailPage || templateCell.Hyperlink.Kind == HyperlinkKind.Custom)) templateCell.Hyperlink.Value = templateCell.Text; resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); } } else { templateCell = CreateCell(item.IsTotal ? Res.Get("ComponentsMisc,Matrix,Total") : item.Value.ToString()); resultCell.RunTimeAssign(templateCell, true); } } private void PrintColumnHeader(MatrixHeaderItem root, int left, int top, int level) { int dataWidth = 1; int height = HeaderHeight; if (Matrix.Data.Cells.Count > 1 && Matrix.CellsSideBySide) { dataWidth = Matrix.Data.Cells.Count; height--; } foreach (MatrixHeaderItem item in root.Items) { Matrix.ColumnValues = item.Values; TableCellData resultCell = ResultTable.GetCellData(left, top); int span = item.Span * dataWidth; int dy = 1; if (item.IsTotal) { dy = height - top; // correct evenStyleIndices for (int i = level + 1; i < evenStyleIndices.Length; i++) evenStyleIndices[i]++; } PrintHeaderCell(item, resultCell, evenStyleIndices[level] % 2 != 0); PrintColumnHeader(item, left, top + dy, level + 1); if (item.PageBreak && left > HeaderWidth) ResultTable.Columns[left].PageBreak = true; left += span; evenStyleIndices[level]++; // set spans after we have printed the cell. This will handle the cell internal objects layout correctly resultCell.ColSpan = span; if (item.IsTotal) { resultCell.RowSpan = height - top; } } // print cell header if (root.Items.Count == 0 && dataWidth > 1) { foreach (MatrixCellDescriptor descr in Matrix.Data.Cells) { TableCell templateCell = null; TableCellData resultCell = ResultTable.GetCellData(left, top); if (root.TemplateColumn != null && descr.TemplateColumn != null && cellHeaderDescriptor.TemplateColumn != null && cellHeaderDescriptor.TemplateRow != null) { templateCell = Matrix[ root.TemplateColumn.Index + (descr.TemplateColumn.Index - cellHeaderDescriptor.TemplateColumn.Index), cellHeaderDescriptor.TemplateRow.Index]; } else templateCell = CreateCell(ExtractColumnName(descr.Expression)); templateCell.SaveState(); templateCell.GetData(); resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); left++; } } } private void PrintColumnHeader() { Matrix.RowValues = null; evenStyleIndices = new int[Matrix.Data.Columns.Count]; PrintColumnHeader(Matrix.Data.Columns.RootItem, HeaderWidth, Matrix.ShowTitle ? 1 : 0, 0); } private void PrintRowHeader(MatrixHeaderItem root, int left, int top, int level) { int dataHeight = 1; int width = HeaderWidth; if (Matrix.Data.Cells.Count > 1 && !Matrix.CellsSideBySide) { dataHeight = Matrix.Data.Cells.Count; width--; } for (int index = 0; index < root.Items.Count; index++) { MatrixHeaderItem item = root.Items[index]; Matrix.RowValues = item.Values; TableCellData resultCell = ResultTable.GetCellData(left, top); int span = item.Span * dataHeight; if (Matrix.SplitRows) { MatrixHeaderItem duplicate = new MatrixHeaderItem(root); duplicate.IsSplitted = true; duplicate.Value = item.Value; duplicate.TemplateRow = item.TemplateRow; duplicate.TemplateCell = item.TemplateCell; duplicate.TemplateColumn = item.TemplateColumn; for (int i = 1; i < span; i++) { root.Items.Insert(index + 1, duplicate); } span = 1; } int dx = 1; if (item.IsTotal) { dx = width - left; // correct evenStyleIndices for (int i = level + 1; i < evenStyleIndices.Length; i++) evenStyleIndices[i]++; } PrintHeaderCell(item, resultCell, evenStyleIndices[level] % 2 != 0); PrintRowHeader(item, left + dx, top, level + 1); if (item.PageBreak && top > HeaderHeight) ResultTable.Rows[top].PageBreak = true; top += span; evenStyleIndices[level]++; // set spans after we have printed the cell. This will handle the cell internal objects layout correctly resultCell.RowSpan = span; if (item.IsTotal) { resultCell.ColSpan = width - left; } } // print cell header if (root.Items.Count == 0 && dataHeight > 1) { foreach (MatrixCellDescriptor descr in Matrix.Data.Cells) { TableCell templateCell = null; TableCellData resultCell = ResultTable.GetCellData(left, top); if (root.TemplateRow != null && descr.TemplateRow != null && cellHeaderDescriptor.TemplateColumn != null && cellHeaderDescriptor.TemplateRow != null) { templateCell = Matrix[cellHeaderDescriptor.TemplateColumn.Index, root.TemplateRow.Index + (descr.TemplateRow.Index - cellHeaderDescriptor.TemplateRow.Index)]; } else templateCell = CreateCell(ExtractColumnName(descr.Expression)); templateCell.SaveState(); templateCell.GetData(); resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); top++; } } } private void PrintRowHeader() { Matrix.ColumnValues = null; evenStyleIndices = new int[Matrix.Data.Rows.Count]; PrintRowHeader(Matrix.Data.Rows.RootItem, 0, HeaderHeight, 0); } private void PrintCorner() { List rowDescr = new List(); List colDescr = new List(); rowDescr.AddRange(Matrix.Data.Rows.ToArray()); colDescr.AddRange(Matrix.Data.Columns.ToArray()); if (Matrix.Data.Cells.Count > 1) { if (Matrix.CellsSideBySide) colDescr.Add(cellHeaderDescriptor); else rowDescr.Add(cellHeaderDescriptor); } int top = 0; bool firstRow = true; foreach (MatrixDescriptor col in colDescr) { int left = 0; foreach (MatrixDescriptor row in rowDescr) { TableCell templateCell = null; if (row.TemplateColumn != null && col.TemplateRow != null) templateCell = Matrix[row.TemplateColumn.Index, col.TemplateRow.Index]; else if (firstRow) templateCell = CreateCell(ExtractColumnName(row.Expression)); if (templateCell != null) { TableCellData resultCell = ResultTable.GetCellData(left, top + (Matrix.ShowTitle ? 1 : 0)); templateCell.SaveState(); if (!designTime) templateCell.GetData(); resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); // do not allow spans to go outside corner area resultCell.ColSpan = Math.Min(templateCell.ColSpan, rowDescr.Count - left); resultCell.RowSpan = Math.Min(templateCell.RowSpan, colDescr.Count - top); } left++; } firstRow = false; top++; } } private void PrintTitle() { TableCell templateCell = titleDescriptor.TemplateCell; if (titleDescriptor.TemplateCell == null) templateCell = CreateCell(Res.Get("ComponentsMisc,Matrix,Title")); TableCellData resultCell = ResultTable.GetCellData(HeaderWidth, 0); templateCell.SaveState(); if (!designTime) templateCell.GetData(); resultCell.RunTimeAssign(templateCell, true); resultCell.ColSpan = ResultTable.ColumnCount - HeaderWidth; templateCell.RestoreState(); // print left-top cell if (titleDescriptor.TemplateCell == null) templateCell.Text = ""; else templateCell = Matrix[0, 0]; resultCell = ResultTable.GetCellData(0, 0); templateCell.SaveState(); if (!designTime) templateCell.GetData(); resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); resultCell.ColSpan = HeaderWidth; } private void PrintHeaders() { PrintColumnHeader(); PrintRowHeader(); if (Matrix.ShowTitle) PrintTitle(); PrintCorner(); } private void PrintData_CalcTotals(int pass) { List columnTerminalItems = Matrix.Data.Columns.RootItem.GetTerminalItems(); List rowTerminalItems = Matrix.Data.Rows.RootItem.GetTerminalItems(); int dataCount = Matrix.Data.Cells.Count; cellValues = new object[dataCount]; foreach (MatrixHeaderItem rowItem in rowTerminalItems) { foreach (MatrixHeaderItem columnItem in columnTerminalItems) { if ((pass == 1 && !(rowItem.IsTotal || columnItem.IsTotal)) || (pass == 2 && (rowItem.IsTotal || columnItem.IsTotal))) continue; // at first we calc cells with non-custom functions // prepare FCellValues array which will be used when calculating custom functions for (int cellIndex = 0; cellIndex < dataCount; cellIndex++) { if (Matrix.Data.Cells[cellIndex].Function != MatrixAggregateFunction.Custom) cellValues[cellIndex] = GetCellValue(columnItem, rowItem, cellIndex); } // at second we calc cells with custom functions // (to allow the custom function to use other cells' values) for (int cellIndex = 0; cellIndex < dataCount; cellIndex++) { if (Matrix.Data.Cells[cellIndex].Function == MatrixAggregateFunction.Custom) cellValues[cellIndex] = GetCellValue(columnItem, rowItem, cellIndex); } Matrix.Data.Cells.SetValues(columnItem.Index, rowItem.Index, cellValues); } } } private void PrintData_CalcPercents() { int dataCount = Matrix.Data.Cells.Count; // check if we need to do this bool hasPercents = false; for (int cellIndex = 0; cellIndex < dataCount; cellIndex++) { if (Matrix.Data.Cells[cellIndex].Percent != MatrixPercent.None) hasPercents = true; } if (!hasPercents) return; List columnTerminalItems = Matrix.Data.Columns.RootItem.GetTerminalItems(); List rowTerminalItems = Matrix.Data.Rows.RootItem.GetTerminalItems(); cellValues = new object[dataCount]; foreach (MatrixHeaderItem rowItem in rowTerminalItems) { foreach (MatrixHeaderItem columnItem in columnTerminalItems) { for (int cellIndex = 0; cellIndex < dataCount; cellIndex++) { object cellValue = Matrix.Data.Cells.GetValue(columnItem.Index, rowItem.Index, cellIndex); object totalValue = null; object value = null; int totalColumnIndex = Matrix.Data.Columns[0].TotalsFirst ? 0 : columnTerminalItems.Count - 1; int totalRowIndex = Matrix.Data.Rows[0].TotalsFirst ? 0 : rowTerminalItems.Count - 1; switch (Matrix.Data.Cells[cellIndex].Percent) { case MatrixPercent.None: value = cellValue; break; case MatrixPercent.ColumnTotal: totalValue = Matrix.Data.Cells.GetValue(columnTerminalItems[totalColumnIndex].Index, rowItem.Index, cellIndex); break; case MatrixPercent.RowTotal: totalValue = Matrix.Data.Cells.GetValue(columnItem.Index, rowTerminalItems[totalRowIndex].Index, cellIndex); break; case MatrixPercent.GrandTotal: totalValue = Matrix.Data.Cells.GetValue(columnTerminalItems[totalColumnIndex].Index, rowTerminalItems[totalRowIndex].Index, cellIndex); break; } if (cellValue != null && totalValue != null) value = ((Variant)(new Variant(cellValue) / new Variant(totalValue))).Value; cellValues[cellIndex] = value; } Matrix.Data.Cells.SetValues(columnItem.Index, rowItem.Index, cellValues); } } } private void PrintData() { // use two passes to calc cell values. This is necessary because this calculation // replaces an array of cell values by the single (aggregated) value. // At the first pass we calc total values only (so they include all cell values, not aggregated ones); // at the second pass we calc other values except total. PrintData_CalcTotals(1); PrintData_CalcTotals(2); // calc percents PrintData_CalcPercents(); // fire AfterTotals event Matrix.OnAfterTotals(EventArgs.Empty); List columnTerminalItems = Matrix.Data.Columns.RootItem.GetTerminalItems(); List rowTerminalItems = Matrix.Data.Rows.RootItem.GetTerminalItems(); int dataCount = Matrix.Data.Cells.Count; int top = HeaderHeight; Point bodyLocation = GetBodyLocation(); bool firstTimePrintingData = true; cellValues = new object[dataCount]; Matrix.RowIndex = 0; foreach (MatrixHeaderItem rowItem in rowTerminalItems) { int left = HeaderWidth; Matrix.RowValues = rowItem.Values; Matrix.ColumnIndex = 0; foreach (MatrixHeaderItem columnItem in columnTerminalItems) { Matrix.ColumnValues = columnItem.Values; for (int cellIndex = 0; cellIndex < dataCount; cellIndex++) { TableCell templateCell = null; TableCellData resultCell = null; MatrixCellDescriptor descr = Matrix.Data.Cells[cellIndex]; if (Matrix.CellsSideBySide) { if (columnItem.TemplateColumn != null && rowItem.TemplateRow != null && descr.TemplateColumn != null) { templateCell = Matrix[ columnItem.TemplateColumn.Index + (descr.TemplateColumn.Index - bodyLocation.X), rowItem.TemplateRow.Index]; } else templateCell = CreateDataCell(); resultCell = ResultTable.GetCellData(left + cellIndex, top); } else { if (columnItem.TemplateColumn != null && rowItem.TemplateRow != null && descr.TemplateColumn != null) { templateCell = Matrix[columnItem.TemplateColumn.Index, rowItem.TemplateRow.Index + (descr.TemplateRow.Index - bodyLocation.Y)]; } else templateCell = CreateDataCell(); resultCell = ResultTable.GetCellData(left, top + cellIndex); } if (designTime) { if (firstTimePrintingData) templateCell.Text = "[" + ExtractColumnName(descr.Expression) + "]"; else templateCell.Text = ""; resultCell.RunTimeAssign(templateCell, true); } else { object value = Matrix.Data.GetValue(columnItem.Index, rowItem.Index, cellIndex); cellValues[cellIndex] = value; templateCell.Text = templateCell.FormatValue(value); templateCell.SaveState(); if (String.IsNullOrEmpty(templateCell.Hyperlink.Expression) && (templateCell.Hyperlink.Kind == HyperlinkKind.DetailReport || templateCell.Hyperlink.Kind == HyperlinkKind.DetailPage || templateCell.Hyperlink.Kind == HyperlinkKind.Custom)) { string hyperlinkValue = ""; string separator = templateCell.Hyperlink.ValuesSeparator; foreach (object obj in Matrix.ColumnValues) { hyperlinkValue += obj.ToString() + separator; } foreach (object obj in Matrix.RowValues) { hyperlinkValue += obj.ToString() + separator; } templateCell.Hyperlink.Value = hyperlinkValue.Substring(0, hyperlinkValue.Length - separator.Length); } int evenStyleIndex = Matrix.MatrixEvenStylePriority == MatrixEvenStylePriority.Rows ? Matrix.RowIndex : Matrix.ColumnIndex; if ((evenStyleIndex + 1) % 2 == 0) templateCell.ApplyEvenStyle(); templateCell.GetData(); resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); } } firstTimePrintingData = false; Matrix.ColumnIndex++; if (Matrix.CellsSideBySide) { if (Matrix.KeepCellsSideBySide) ResultTable.Columns[left].KeepColumns = dataCount; left += dataCount; } else left++; } Matrix.RowIndex++; if (Matrix.CellsSideBySide) top++; else top += dataCount; } } private object GetCellValue(MatrixHeaderItem columnItem, MatrixHeaderItem rowItem, int cellIndex) { if (columnItem.IsTotal || rowItem.IsTotal) return GetAggregatedTotalValue(columnItem, rowItem, cellIndex); else return GetAggregatedValue(Matrix.Data.GetValues(columnItem.Index, rowItem.Index, cellIndex), cellIndex); } private object GetAggregatedTotalValue(MatrixHeaderItem column, MatrixHeaderItem row, int cellIndex) { ArrayList list = new ArrayList(); if (column.IsTotal) column = column.Parent; if (row.IsTotal) row = row.Parent; List columnTerminalItems = column.GetTerminalItems(); List rowTerminalItems = row.GetTerminalItems(); // collect all values in the specified items foreach (MatrixHeaderItem rowItem in rowTerminalItems) { if (rowItem.IsTotal) continue; foreach (MatrixHeaderItem columnItem in columnTerminalItems) { if (columnItem.IsTotal) continue; ArrayList values = Matrix.Data.GetValues(columnItem.Index, rowItem.Index, cellIndex); if (values != null) list.AddRange(values); } } return GetAggregatedValue(list, cellIndex); } private object GetAggregatedValue(ArrayList list, int cellIndex) { if (list == null || list.Count == 0) return null; MatrixAggregateFunction function = Matrix.Data.Cells[cellIndex].Function; // custom function - just calculate the expression if (function == MatrixAggregateFunction.Custom) return Report.Calc(Matrix.Data.Cells[cellIndex].Expression); // Count function - just return the number of values if (function == MatrixAggregateFunction.Count) return list.Count; if (function == MatrixAggregateFunction.CountDistinct) { Hashtable distinctValues = new Hashtable(); foreach (object value in list) { distinctValues[value] = 1; } return distinctValues.Keys.Count; } // aggregated value Variant aggrValue = new Variant(); for (int i = 0; i < list.Count; i++) { object value = list[i]; if (i == 0) { // assign the first value to aggrValue aggrValue = new Variant(value); } else { // handle other values switch (function) { case MatrixAggregateFunction.Sum: case MatrixAggregateFunction.Avg: aggrValue += new Variant(value); break; case MatrixAggregateFunction.Min: if (new Variant(value) < aggrValue) aggrValue = new Variant(value); break; case MatrixAggregateFunction.Max: if (new Variant(value) > aggrValue) aggrValue = new Variant(value); break; } } } // finish Avg calculation if (function == MatrixAggregateFunction.Avg) aggrValue = aggrValue / list.Count; return aggrValue.Value; } #endregion #region Public Methods public void BuildTemplate() { bool noColumns = Matrix.Data.Columns.Count == 0; bool noRows = Matrix.Data.Rows.Count == 0; bool noCells = Matrix.Data.Cells.Count == 0; // create temporary descriptors if (noColumns) Matrix.Data.Columns.Add(noColumnsDescriptor); if (noRows) Matrix.Data.Rows.Add(noRowsDescriptor); if (noCells) Matrix.Data.Cells.Add(noCellsDescriptor); UpdateTemplateSizes(); // prepare data for the result table string[] columnValues = new string[Matrix.Data.Columns.Count]; string[] rowValues = new string[Matrix.Data.Rows.Count]; object[] cellValues = new object[Matrix.Data.Cells.Count]; for (int i = 0; i < Matrix.Data.Columns.Count; i++) { columnValues[i] = "[" + ExtractColumnName(Matrix.Data.Columns[i].Expression) + "]"; } for (int i = 0; i < Matrix.Data.Rows.Count; i++) { rowValues[i] = "[" + ExtractColumnName(Matrix.Data.Rows[i].Expression) + "]"; } Matrix.Data.Clear(); Matrix.Data.AddValue(columnValues, rowValues, cellValues, 0); // create the result table designTime = true; resultTable = new TableResult(); InitResultTable(true); PrintHeaders(); PrintData(); // copy the result table to the Matrix Matrix.ColumnCount = ResultTable.ColumnCount; Matrix.RowCount = ResultTable.RowCount; Matrix.FixedColumns = HeaderWidth; Matrix.FixedRows = HeaderHeight; Matrix.CreateUniqueNames(); for (int x = 0; x < Matrix.ColumnCount; x++) { Matrix.Columns[x].Assign(ResultTable.Columns[x]); } for (int y = 0; y < Matrix.RowCount; y++) { Matrix.Rows[y].Assign(ResultTable.Rows[y]); } for (int x = 0; x < Matrix.ColumnCount; x++) { for (int y = 0; y < Matrix.RowCount; y++) { TableCell cell = Matrix[x, y]; // call AssignAll with assignName parameter to preserve names of objects contained in a cell ResultTable[x, y].Name = cell.Name; cell.AssignAll(ResultTable[x, y], true); cell.SetFlags(Flags.CanEdit, true); } } UpdateDescriptors(); resultTable.Dispose(); // clear temporary descriptors, set hints if (noColumns) { SetHint(Matrix[HeaderWidth, Matrix.ShowTitle ? 1 : 0], Res.Get("ComponentsMisc,Matrix,NewColumn")); Matrix.Data.Columns.Clear(); } else { noColumnsDescriptor.TemplateColumn = Matrix.Data.Columns[0].TemplateColumn; noColumnsDescriptor.TemplateRow = Matrix.Data.Columns[0].TemplateRow; noColumnsDescriptor.TemplateCell = Matrix.Data.Columns[0].TemplateCell; } if (noRows) { SetHint(Matrix[0, HeaderHeight], Res.Get("ComponentsMisc,Matrix,NewRow")); Matrix.Data.Rows.Clear(); } else { noRowsDescriptor.TemplateColumn = Matrix.Data.Rows[0].TemplateColumn; noRowsDescriptor.TemplateRow = Matrix.Data.Rows[0].TemplateRow; noRowsDescriptor.TemplateCell = Matrix.Data.Rows[0].TemplateCell; } if (noCells) { SetHint(Matrix[HeaderWidth, HeaderHeight], Res.Get("ComponentsMisc,Matrix,NewCell")); Matrix.Data.Cells.Clear(); } else { noCellsDescriptor.TemplateColumn = Matrix.Data.Cells[0].TemplateColumn; noCellsDescriptor.TemplateRow = Matrix.Data.Cells[0].TemplateRow; noCellsDescriptor.TemplateCell = Matrix.Data.Cells[0].TemplateCell; } } public void UpdateDescriptors() { bool noColumns = Matrix.Data.Columns.Count == 0; bool noRows = Matrix.Data.Rows.Count == 0; bool noCells = Matrix.Data.Cells.Count == 0; // create temporary descriptors if (noColumns) Matrix.Data.Columns.Add(noColumnsDescriptor); if (noRows) Matrix.Data.Rows.Add(noRowsDescriptor); if (noCells) Matrix.Data.Cells.Add(noCellsDescriptor); UpdateTemplateSizes(); Matrix.FixedColumns = HeaderWidth; Matrix.FixedRows = HeaderHeight; UpdateColumnDescriptors(); UpdateRowDescriptors(); UpdateCellDescriptors(); UpdateOtherDescriptors(); // clear temporary descriptors if (noColumns) Matrix.Data.Columns.Clear(); if (noRows) Matrix.Data.Rows.Clear(); if (noCells) Matrix.Data.Cells.Clear(); } public void StartPrint() { if ((Matrix.Data.Columns.Count == 0 && Matrix.Data.Rows.Count == 0) || Matrix.Data.Cells.Count == 0) throw new Exception(String.Format(Res.Get("Messages,MatrixError"), Matrix.Name)); designTime = false; noColumns = Matrix.Data.Columns.Count == 0; noRows = Matrix.Data.Rows.Count == 0; // create temporary descriptors if (noColumns) Matrix.Data.Columns.Add(noColumnsDescriptor); if (noRows) Matrix.Data.Rows.Add(noRowsDescriptor); Config.ReportSettings.OnProgress(Report, Res.Get("ComponentsMisc,Matrix,FillData"), 0, 0); Matrix.Data.Clear(); Matrix.OnManualBuild(EventArgs.Empty); } public void AddDataRow() { object[] columnValues = new object[Matrix.Data.Columns.Count]; object[] rowValues = new object[Matrix.Data.Rows.Count]; object[] cellValues = new object[Matrix.Data.Cells.Count]; string expression = ""; for (int i = 0; i < Matrix.Data.Columns.Count; i++) { expression = Matrix.Data.Columns[i].Expression; columnValues[i] = String.IsNullOrEmpty(expression) ? null : Report.Calc(expression); } for (int i = 0; i < Matrix.Data.Rows.Count; i++) { expression = Matrix.Data.Rows[i].Expression; rowValues[i] = String.IsNullOrEmpty(expression) ? null : Report.Calc(expression); } for (int i = 0; i < Matrix.Data.Cells.Count; i++) { // skip custom function calculation; it will be calculated when we print the value if (Matrix.Data.Cells[i].Function == MatrixAggregateFunction.Custom) cellValues[i] = 0; else cellValues[i] = Report.Calc(Matrix.Data.Cells[i].Expression); } Matrix.Data.AddValue(columnValues, rowValues, cellValues, Matrix.DataSource.CurrentRowNo); } public void AddEmptyDataRow() { object[] columnValues = new object[Matrix.Data.Columns.Count]; object[] rowValues = new object[Matrix.Data.Rows.Count]; object[] cellValues = new object[Matrix.Data.Cells.Count]; Matrix.Data.AddValue(columnValues, rowValues, cellValues, 0); } public void AddDataRows() { if (Matrix.DataSource != null) { Matrix.DataSource.Init(Matrix.Filter); while (Matrix.DataSource.HasMoreRows) { AddDataRow(); Matrix.DataSource.Next(); } } } public void FinishPrint() { if (!Matrix.Data.IsEmpty || Matrix.PrintIfEmpty) { if (Matrix.Data.IsEmpty) AddEmptyDataRow(); UpdateDescriptors(); ResultTable.FixedColumns = HeaderWidth; ResultTable.FixedRows = HeaderHeight; InitResultTable(false); PrintHeaders(); PrintData(); } // clear temporary descriptors if (noColumns) Matrix.Data.Columns.Clear(); if (noRows) Matrix.Data.Rows.Clear(); } public void UpdateStyle() { for (int y = 0; y < Matrix.RowCount; y++) { for (int x = 0; x < Matrix.ColumnCount; x++) { string style = x < HeaderWidth || y < HeaderHeight ? "Header" : "Body"; ApplyStyle(Matrix[x, y], style); } } } #endregion public MatrixHelper(MatrixObject matrix) { this.matrix = matrix; titleDescriptor = new MatrixDescriptor(); cellHeaderDescriptor = new MatrixDescriptor(); cellHeaderDescriptor.Expression = "Data"; noColumnsDescriptor = new MatrixHeaderDescriptor("", false); noRowsDescriptor = new MatrixHeaderDescriptor("", false); noCellsDescriptor = new MatrixCellDescriptor(); } } }