ResultBuilder.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using FastReport.Gauge;
  5. using FastReport.Gauge.Simple.Progress;
  6. #if MSCHART
  7. using FastReport.MSChart;
  8. #endif
  9. using FastReport.Table;
  10. namespace FastReport.AdvMatrix
  11. {
  12. internal static class ResultBuilder
  13. {
  14. private static int EnumColumnData(HeaderData root, int columnIndex, int rowIndex, Action<HeaderData, int, int, bool> func)
  15. {
  16. foreach (HeaderData d in root)
  17. {
  18. func(d, columnIndex, rowIndex, true);
  19. if (d.Items.Count > 0)
  20. {
  21. if (d.Descriptor.Stepped)
  22. columnIndex = EnumColumnData(d, columnIndex + 1, rowIndex, func);
  23. else
  24. columnIndex = EnumColumnData(d, columnIndex, rowIndex + 1, func);
  25. }
  26. columnIndex++;
  27. }
  28. return columnIndex - 1;
  29. }
  30. private static int EnumRowData(HeaderData root, int columnIndex, int rowIndex, Action<HeaderData, int, int, bool> func)
  31. {
  32. foreach (HeaderData d in root)
  33. {
  34. func(d, columnIndex, rowIndex, false);
  35. if (d.Items.Count > 0)
  36. {
  37. if (d.Descriptor.Stepped)
  38. rowIndex = EnumRowData(d, columnIndex, rowIndex + 1, func);
  39. else
  40. rowIndex = EnumRowData(d, columnIndex + 1, rowIndex, func);
  41. }
  42. rowIndex++;
  43. }
  44. return rowIndex - 1;
  45. }
  46. private static void PrintHeaderCell(AdvMatrixObject matrix, HeaderData item, TableCellData resultCell, bool isColumn, bool isEven)
  47. {
  48. HeaderDescriptor descr = item.Descriptor;
  49. TableCell templateCell = descr.TemplateCell;
  50. if (matrix.DataSource != null)
  51. matrix.DataSource.CurrentRowNo = item.DataRowNo;
  52. matrix.RowNo = item.RowNo;
  53. matrix.ItemCount = item.ItemCount;
  54. MatrixCollapseButton btn = templateCell.GetCollapseButton();
  55. if (btn != null)
  56. matrix.ItemCount = btn.GetLinkedItemsCount(item);
  57. templateCell.SaveState();
  58. if (isEven)
  59. templateCell.ApplyEvenStyle();
  60. object value = item.Value;
  61. if (value != null)
  62. {
  63. templateCell.SetValue(value);
  64. templateCell.Text = templateCell.Format.FormatValue(value);
  65. }
  66. if (!String.IsNullOrEmpty(descr.DisplayText))
  67. {
  68. templateCell.Text = descr.DisplayText;
  69. templateCell.AllowExpressions = true;
  70. }
  71. templateCell.GetData();
  72. if (String.IsNullOrEmpty(templateCell.Hyperlink.Expression) &&
  73. (templateCell.Hyperlink.Kind == HyperlinkKind.DetailReport ||
  74. templateCell.Hyperlink.Kind == HyperlinkKind.DetailPage ||
  75. templateCell.Hyperlink.Kind == HyperlinkKind.Custom))
  76. templateCell.Hyperlink.Value = templateCell.Text;
  77. if (templateCell.Objects != null)
  78. {
  79. foreach (Base c in templateCell.Objects)
  80. {
  81. if (c is MatrixButton)
  82. {
  83. MatrixButton b = c as MatrixButton;
  84. b.IsColumn = isColumn;
  85. b.Index = item.Index;
  86. }
  87. if (c is MatrixCollapseButton)
  88. {
  89. MatrixCollapseButton b = c as MatrixCollapseButton;
  90. foreach (HeaderDataList dl in b.GetLinkedItems(item))
  91. {
  92. b.Collapsed = !dl.Visible;
  93. break;
  94. }
  95. }
  96. if (c is MatrixSortButton)
  97. {
  98. MatrixSortButton b = c as MatrixSortButton;
  99. b.Sort = SortOrder.None;
  100. InteractiveSortInfo info = matrix.Data.InteractiveSort;
  101. if (isColumn && item.Index == info.Column.Index)
  102. b.Sort = info.Column.Sort;
  103. if (!isColumn && item.Index == info.Row.Index)
  104. b.Sort = info.Row.Sort;
  105. }
  106. }
  107. }
  108. resultCell.RunTimeAssign(templateCell, true);
  109. templateCell.RestoreState();
  110. }
  111. private static void PrintCornerCell(TableCell templateCell, TableCellData resultCell)
  112. {
  113. templateCell.SaveState();
  114. templateCell.GetData();
  115. resultCell.RunTimeAssign(templateCell, true);
  116. templateCell.RestoreState();
  117. }
  118. private static void PrintDataCell(AdvMatrixObject matrix, TableCell templateCell, TableCellData resultCell, bool isEven)
  119. {
  120. templateCell.SaveState();
  121. if (isEven)
  122. templateCell.ApplyEvenStyle();
  123. CellDescriptor descriptor = templateCell.GetDescriptor();
  124. switch (descriptor.ContentType)
  125. {
  126. case CellContentType.MixedText:
  127. templateCell.AllowExpressions = true;
  128. templateCell.Text = descriptor.Text;
  129. break;
  130. case CellContentType.Aggregate:
  131. templateCell.SetValue(descriptor.Aggregates[0].GetValue(matrix.Data.Context.ColumnItem.Index, matrix.Data.Context.RowItem.Index));
  132. templateCell.Text = templateCell.FormatValue(templateCell.Value);
  133. break;
  134. case CellContentType.SingleExpression:
  135. templateCell.SetValue(matrix.Report.Calc(descriptor.Text));
  136. templateCell.Text = templateCell.FormatValue(templateCell.Value);
  137. break;
  138. }
  139. templateCell.GetData();
  140. ProcessCellObjects(templateCell);
  141. if (String.IsNullOrEmpty(templateCell.Hyperlink.Expression) &&
  142. (templateCell.Hyperlink.Kind == HyperlinkKind.DetailReport ||
  143. templateCell.Hyperlink.Kind == HyperlinkKind.DetailPage ||
  144. templateCell.Hyperlink.Kind == HyperlinkKind.Custom))
  145. {
  146. string hyperlinkValue = "";
  147. string separator = templateCell.Hyperlink.ValuesSeparator;
  148. foreach (object obj in matrix.ColumnValues)
  149. {
  150. hyperlinkValue += obj.ToString() + separator;
  151. }
  152. foreach (object obj in matrix.RowValues)
  153. {
  154. hyperlinkValue += obj.ToString() + separator;
  155. }
  156. templateCell.Hyperlink.Value = hyperlinkValue.Substring(0, hyperlinkValue.Length - separator.Length);
  157. }
  158. resultCell.RunTimeAssign(templateCell, true);
  159. templateCell.RestoreState();
  160. resultCell.ColSpan = templateCell.ColSpan;
  161. resultCell.RowSpan = templateCell.RowSpan;
  162. }
  163. private static IList ReduceValues(this IList list, int valuesCount)
  164. {
  165. int ratio = list.Count / valuesCount;
  166. if (ratio < 2)
  167. return list;
  168. ArrayList reducedList = new ArrayList();
  169. for (int i = 0; i < list.Count; i += ratio)
  170. {
  171. dynamic v = list[i];
  172. for (int j = 0; j < ratio && i + j < list.Count; j++)
  173. {
  174. if ((dynamic)list[i + j] > v)
  175. v = list[i + j];
  176. }
  177. reducedList.Add(v);
  178. }
  179. return reducedList;
  180. }
  181. private static void ProcessCellObjects(TableCell templateCell)
  182. {
  183. if (templateCell.Objects != null)
  184. {
  185. foreach (ReportComponentBase c in templateCell.Objects)
  186. {
  187. #if MSCHART
  188. MSChartObject chart = c as MSChartObject;
  189. if (chart != null && chart.Series.Count == 1)
  190. {
  191. chart.Series[0].ClearValues();
  192. if (templateCell.Value is IList)
  193. {
  194. IList list = (templateCell.Value as IList).ReduceValues((int)chart.Width / 3);
  195. for (int i = 0; i < list.Count; i++)
  196. {
  197. chart.Series[0].AddValue(i, list[i]);
  198. }
  199. }
  200. templateCell.Text = "";
  201. }
  202. #endif
  203. GaugeObject gauge = c as GaugeObject;
  204. if (gauge != null)
  205. {
  206. if (templateCell.Value == null || templateCell.Value is DBNull)
  207. gauge.Visible = false;
  208. else
  209. gauge.Value = (double)(dynamic)templateCell.Value;
  210. templateCell.Text = "";
  211. }
  212. }
  213. }
  214. }
  215. public static void BuildResult(AdvMatrixObject matrix)
  216. {
  217. TableResult resultTable = matrix.ResultTable;
  218. int cornerWidth = matrix.Data.Rows.Size;
  219. int cornerHeight = matrix.Data.Columns.Size;
  220. resultTable.FixedColumns = cornerWidth;
  221. resultTable.FixedRows = cornerHeight;
  222. // rows/columns in the corner area. Enumerate on descriptors to do it fast.
  223. // We need this code because some rows and columns may be invisible due to its collapsed state,
  224. // so its properties will not be assigned to resultTable
  225. resultTable.ColumnCount = cornerWidth;
  226. resultTable.RowCount = cornerHeight;
  227. Action<HeaderDescriptor, int, int, bool> func = (d, columnIndex, rowIndex, isColumn) =>
  228. {
  229. if (!isColumn && d.TemplateColumn != null)
  230. resultTable.Columns[columnIndex].Assign(d.TemplateColumn);
  231. if (isColumn && d.TemplateRow != null)
  232. resultTable.Rows[rowIndex].Assign(d.TemplateRow);
  233. };
  234. TemplateBuilder.EnumColumnDescriptors(matrix.Data.Columns.Descriptor, cornerWidth, 0, func);
  235. TemplateBuilder.EnumRowDescriptors(matrix.Data.Rows.Descriptor, 0, cornerHeight, func);
  236. // rows/columns
  237. Action<HeaderData, int, int, bool> func1 = (data, columnIndex, rowIndex, isColumn) =>
  238. {
  239. // create resultTable columns and rows
  240. while (columnIndex >= resultTable.Columns.Count)
  241. resultTable.Columns.Add(new TableColumn());
  242. while (rowIndex >= resultTable.Rows.Count)
  243. resultTable.Rows.Add(new TableRow());
  244. // assign properties from existing template columns/rows.
  245. // The same assignment may occur several times, but it's a cheap operation so it's ok
  246. HeaderDescriptor d = data.Descriptor;
  247. if (isColumn && d.TemplateColumn != null)
  248. resultTable.Columns[columnIndex].Assign(d.TemplateColumn);
  249. if (!isColumn && d.TemplateRow != null)
  250. resultTable.Rows[rowIndex].Assign(d.TemplateRow);
  251. // page breaks
  252. if (d.PageBreak && data.RowNo > 1)
  253. {
  254. if (isColumn)
  255. resultTable.Columns[columnIndex].PageBreak = true;
  256. else
  257. resultTable.Rows[rowIndex].PageBreak = true;
  258. }
  259. };
  260. EnumColumnData(matrix.Data.Columns.Data, cornerWidth, 0, func1);
  261. EnumRowData(matrix.Data.Rows.Data, 0, cornerHeight, func1);
  262. // corner cells
  263. for (int left = 0; left < cornerWidth; left++)
  264. {
  265. for (int top = 0; top < cornerHeight; top++)
  266. {
  267. TableCellData resultCell = resultTable.GetCellData(left, top);
  268. TableCell templateCell = matrix[left, top];
  269. PrintCornerCell(templateCell, resultCell);
  270. // do not allow spans to go outside corner area. Fix template cells as well (else we will get empty cells in the header area)
  271. templateCell.ColSpan = Math.Min(templateCell.ColSpan, cornerWidth - left);
  272. templateCell.RowSpan = Math.Min(templateCell.RowSpan, cornerHeight - top);
  273. resultCell.ColSpan = templateCell.ColSpan;
  274. resultCell.RowSpan = templateCell.RowSpan;
  275. }
  276. }
  277. // header cells
  278. Action<HeaderData, int, int, bool> func2 = (data, columnIndex, rowIndex, isColumn) =>
  279. {
  280. TableCellData resultCell = resultTable.GetCellData(columnIndex, rowIndex);
  281. // required check for MergeSingleItem
  282. if (resultTable.IsInsideSpan(resultCell.Cell))
  283. return;
  284. PrintHeaderCell(matrix, data, resultCell, isColumn, (isColumn ? columnIndex - cornerWidth : rowIndex - cornerHeight) % 2 == 0);
  285. HeaderDescriptor descr = data.Descriptor;
  286. if (isColumn)
  287. {
  288. resultCell.ColSpan = data.Span;
  289. if (data.Items.Count == 0 || (descr.MergeSingleItem && data.TerminalItems.Count == 1) || descr.Stepped)
  290. resultCell.RowSpan = cornerHeight - rowIndex;
  291. else if (descr.MergeSingleItem && data.ItemCount == 1)
  292. resultCell.RowSpan = 2;
  293. }
  294. else
  295. {
  296. resultCell.RowSpan = data.Span;
  297. if (data.Items.Count == 0 || (descr.MergeSingleItem && data.TerminalItems.Count == 1) || descr.Stepped)
  298. resultCell.ColSpan = cornerWidth - columnIndex;
  299. else if (descr.MergeSingleItem && data.ItemCount == 1)
  300. resultCell.ColSpan = 2;
  301. }
  302. if (descr.ColSpan > 0)
  303. resultCell.ColSpan = descr.ColSpan;
  304. if (descr.RowSpan > 0)
  305. resultCell.RowSpan = descr.RowSpan;
  306. };
  307. EnumColumnData(matrix.Data.Columns.Data, cornerWidth, 0, func2);
  308. EnumRowData(matrix.Data.Rows.Data, 0, cornerHeight, func2);
  309. // data cells
  310. List<HeaderData> columnTerminalItems = matrix.Data.Columns.Data.TerminalItems;
  311. List<HeaderData> rowTerminalItems = matrix.Data.Rows.Data.TerminalItems;
  312. matrix.ColumnIndex = 0;
  313. foreach (HeaderData columnItem in columnTerminalItems)
  314. {
  315. matrix.RowIndex = 0;
  316. matrix.ColumnValues = columnItem.Values;
  317. foreach (HeaderData rowItem in rowTerminalItems)
  318. {
  319. matrix.RowValues = rowItem.Values;
  320. TableCellData resultCell = resultTable.GetCellData(matrix.ColumnIndex + cornerWidth, matrix.RowIndex + cornerHeight);
  321. TableCell templateCell = matrix[columnItem.Descriptor.TemplateColumn.Index, rowItem.Descriptor.TemplateRow.Index];
  322. // set context required by aggregate calculation
  323. matrix.Data.Context.Descriptor = templateCell.GetDescriptor();
  324. matrix.Data.Context.ColumnItem = columnItem;
  325. matrix.Data.Context.RowItem = rowItem;
  326. if (matrix.DataSource != null)
  327. matrix.DataSource.CurrentRowNo = matrix.DataRowPriority == HeaderPriority.Columns ? columnItem.DataRowNo : rowItem.DataRowNo;
  328. int evenStyleIndex = matrix.EvenStylePriority == HeaderPriority.Columns ? matrix.ColumnIndex : matrix.RowIndex;
  329. PrintDataCell(matrix, templateCell, resultCell, evenStyleIndex % 2 == 0);
  330. matrix.RowIndex++;
  331. }
  332. matrix.ColumnIndex++;
  333. }
  334. }
  335. }
  336. }