ReportEngine.Bands.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. using System;
  2. using System.Collections.Generic;
  3. namespace FastReport.Engine
  4. {
  5. public partial class ReportEngine
  6. {
  7. #region Fields
  8. private BandBase outputBand;
  9. #endregion Fields
  10. #region Private Methods
  11. private void PrepareBand(BandBase band, bool getData)
  12. {
  13. if (band.Visible)
  14. {
  15. if (getData)
  16. {
  17. band.GetData();
  18. }
  19. TranslateObjects(band);
  20. RenderInnerSubreports(band);
  21. band.CalcHeight();
  22. }
  23. }
  24. private float CalcHeight(BandBase band)
  25. {
  26. // band is already prepared, its Height is ready to use
  27. if (band.IsRunning)
  28. return band.Height;
  29. band.SaveState();
  30. try
  31. {
  32. PrepareBand(band, true);
  33. return band.Height;
  34. }
  35. finally
  36. {
  37. band.RestoreState();
  38. }
  39. }
  40. private BandBase CloneBand(BandBase band)
  41. {
  42. // clone a band and all its objects
  43. BandBase cloneBand = Activator.CreateInstance(band.GetType()) as BandBase;
  44. cloneBand.Assign(band);
  45. cloneBand.SetReport(Report);
  46. cloneBand.SetRunning(true);
  47. foreach (ReportComponentBase obj in band.Objects)
  48. {
  49. ReportComponentBase cloneObj = Activator.CreateInstance(obj.GetType()) as ReportComponentBase;
  50. cloneObj.AssignAll(obj);
  51. cloneBand.Objects.Add(cloneObj);
  52. }
  53. return cloneBand;
  54. }
  55. private void AddToOutputBand(BandBase band, bool getData)
  56. {
  57. band.SaveState();
  58. try
  59. {
  60. PrepareBand(band, getData);
  61. if (band.Visible)
  62. {
  63. outputBand.SetRunning(true);
  64. BandBase cloneBand = CloneBand(band);
  65. cloneBand.Left = CurX;
  66. cloneBand.Top = CurY;
  67. cloneBand.Parent = outputBand;
  68. CurY += cloneBand.Height;
  69. }
  70. }
  71. finally
  72. {
  73. band.RestoreState();
  74. }
  75. }
  76. private void ShowBandToPreparedPages(BandBase band, bool getData)
  77. {
  78. // handle "StartNewPage". Skip if it's the first row, avoid empty first page.
  79. if ((band.StartNewPage && !(band.Parent is PageHeaderBand || band.Parent is PageFooterBand)) && band.FlagUseStartNewPage && (band.RowNo != 1 || band.FirstRowStartsNewPage) &&
  80. !band.Repeated)
  81. {
  82. EndColumn();
  83. }
  84. band.SaveState();
  85. try
  86. {
  87. PrepareBand(band, getData);
  88. if (band.Visible)
  89. {
  90. if (BandHasHardPageBreaks(band))
  91. {
  92. foreach (var b in SplitHardPageBreaks(band))
  93. {
  94. if (b.StartNewPage)
  95. EndColumn();
  96. AddToPreparedPages(b);
  97. }
  98. }
  99. else
  100. {
  101. AddToPreparedPages(band);
  102. }
  103. }
  104. }
  105. finally
  106. {
  107. band.RestoreState();
  108. }
  109. }
  110. private void ShowBand(BandBase band, BandBase outputBand, float offsetX, float offsetY)
  111. {
  112. float saveCurX = CurX;
  113. float saveCurY = CurY;
  114. BandBase saveOutputBand = this.outputBand;
  115. CurX = offsetX;
  116. CurY = offsetY;
  117. try
  118. {
  119. this.outputBand = outputBand;
  120. ShowBand(band);
  121. }
  122. finally
  123. {
  124. this.outputBand = saveOutputBand;
  125. CurX = saveCurX;
  126. CurY = saveCurY;
  127. }
  128. }
  129. /// <summary>
  130. /// Shows band at the current position.
  131. /// </summary>
  132. /// <param name="band">Band to show.</param>
  133. /// <remarks>
  134. /// After the band is shown, the current position is advanced by the band's height.
  135. /// </remarks>
  136. public void ShowBand(BandBase band)
  137. {
  138. if (band != null)
  139. for (int i = 0; i < band.RepeatBandNTimes; i++)
  140. ShowBand(band, true);
  141. }
  142. private void ShowBand(BandBase band, bool getData)
  143. {
  144. if (band == null)
  145. return;
  146. BandBase saveCurBand = curBand;
  147. curBand = band;
  148. try
  149. {
  150. // do we need to keep child?
  151. ChildBand child = band.Child;
  152. bool showChild = child != null && !(band is DataBand && child.CompleteToNRows > 0) && !child.FillUnusedSpace &&
  153. !(band is DataBand && child.PrintIfDatabandEmpty);
  154. if (showChild && band.KeepChild)
  155. {
  156. StartKeep(band);
  157. }
  158. if (outputBand != null)
  159. {
  160. AddToOutputBand(band, getData);
  161. }
  162. else
  163. {
  164. ShowBandToPreparedPages(band, getData);
  165. }
  166. ProcessTotals(band);
  167. if (band.Visible)
  168. {
  169. RenderOuterSubreports(band);
  170. }
  171. // show child band. Skip if child is used to fill empty space: it was processed already
  172. if (showChild)
  173. {
  174. ShowBand(child);
  175. if (band.KeepChild)
  176. {
  177. EndKeep();
  178. }
  179. }
  180. }
  181. finally
  182. {
  183. curBand = saveCurBand;
  184. }
  185. }
  186. private void ProcessTotals(BandBase band)
  187. {
  188. Report.Dictionary.Totals.ProcessBand(band);
  189. }
  190. #endregion Private Methods
  191. #region Internal Methods
  192. internal bool CanPrint(ReportComponentBase obj)
  193. {
  194. // Apply visible expression if needed.
  195. if (!String.IsNullOrEmpty(obj.VisibleExpression))
  196. {
  197. object expression = null;
  198. // Calculate expressions with TotalPages only on FinalPass.
  199. if (!obj.VisibleExpression.Contains("TotalPages") || (Report.DoublePass && FinalPass))
  200. {
  201. expression = Report.Calc(Code.CodeUtils.FixExpressionWithBrackets(obj.VisibleExpression));
  202. }
  203. if (expression != null && expression is bool)
  204. {
  205. if (!obj.VisibleExpression.Contains("TotalPages"))
  206. {
  207. obj.Visible = (bool)expression;
  208. }
  209. else if (FirstPass)
  210. {
  211. obj.Visible = true;
  212. }
  213. else
  214. {
  215. obj.Visible = (bool)expression;
  216. }
  217. }
  218. }
  219. // Apply exportable expression if needed.
  220. if (!String.IsNullOrEmpty(obj.ExportableExpression))
  221. {
  222. object expression = null;
  223. expression = Report.Calc(Code.CodeUtils.FixExpressionWithBrackets(obj.ExportableExpression));
  224. if (expression is bool)
  225. {
  226. obj.Exportable = (bool)expression;
  227. }
  228. }
  229. // Apply printable expression if needed.
  230. if (!String.IsNullOrEmpty(obj.PrintableExpression))
  231. {
  232. object expression = null;
  233. expression = Report.Calc(Code.CodeUtils.FixExpressionWithBrackets(obj.PrintableExpression));
  234. if (expression is bool)
  235. {
  236. obj.Printable = (bool)expression;
  237. }
  238. }
  239. if (!obj.Visible || !obj.FlagPreviewVisible)
  240. {
  241. return false;
  242. }
  243. bool isFirstPage = CurPage == firstReportPage;
  244. bool isLastPage = CurPage == TotalPages - 1;
  245. bool isRepeated = obj.Band != null && obj.Band.Repeated;
  246. bool canPrint = false;
  247. if ((obj.PrintOn & PrintOn.OddPages) > 0 && CurPage % 2 == 1)
  248. {
  249. canPrint = true;
  250. }
  251. if ((obj.PrintOn & PrintOn.EvenPages) > 0 && CurPage % 2 == 0)
  252. {
  253. canPrint = true;
  254. }
  255. if (isLastPage)
  256. {
  257. if ((obj.PrintOn & PrintOn.LastPage) == 0)
  258. {
  259. canPrint = false;
  260. }
  261. if (obj.PrintOn == PrintOn.LastPage || obj.PrintOn == (PrintOn.LastPage | PrintOn.SinglePage) ||
  262. obj.PrintOn == (PrintOn.FirstPage | PrintOn.LastPage))
  263. {
  264. canPrint = true;
  265. }
  266. }
  267. if (isFirstPage)
  268. {
  269. if ((obj.PrintOn & PrintOn.FirstPage) == 0)
  270. {
  271. canPrint = false;
  272. }
  273. if (obj.PrintOn == PrintOn.FirstPage || obj.PrintOn == (PrintOn.FirstPage | PrintOn.SinglePage) ||
  274. obj.PrintOn == (PrintOn.FirstPage | PrintOn.LastPage))
  275. {
  276. canPrint = true;
  277. }
  278. }
  279. if (isFirstPage && isLastPage)
  280. {
  281. canPrint = (obj.PrintOn & PrintOn.SinglePage) > 0;
  282. }
  283. if (isRepeated)
  284. {
  285. canPrint = (obj.PrintOn & PrintOn.RepeatedBand) > 0;
  286. }
  287. return canPrint;
  288. }
  289. internal void AddToPreparedPages(BandBase band)
  290. {
  291. bool isReportSummary = band is ReportSummaryBand;
  292. // check if band is service band (e.g. page header/footer/overlay).
  293. BandBase mainBand = band;
  294. // for child bands, check its parent band.
  295. if (band is ChildBand)
  296. {
  297. mainBand = (band as ChildBand).GetTopParentBand;
  298. }
  299. bool isPageBand = mainBand is PageHeaderBand || mainBand is PageFooterBand || mainBand is OverlayBand;
  300. bool isColumnBand = mainBand is ColumnHeaderBand || mainBand is ColumnFooterBand;
  301. // check if we have enough space for a band.
  302. bool checkFreeSpace = !isPageBand && !isColumnBand && band.FlagCheckFreeSpace;
  303. if (checkFreeSpace && FreeSpace < band.Height)
  304. {
  305. // we don't have enough space. What should we do?
  306. // - if band can break, break it
  307. // - if band cannot break, check the band height:
  308. // - it's the first row of a band and is bigger than page: break it immediately.
  309. // - in other case, add a new page/column and tell the band that it must break next time.
  310. if (band.CanBreak || band.FlagMustBreak || (band.AbsRowNo == 1 && band.Height > PageHeight - PageFooterHeight))
  311. {
  312. // since we don't show the column footer band in the EndLastPage, do it here.
  313. if (isReportSummary)
  314. {
  315. ShowReprintFooters();
  316. ShowBand(page.ColumnFooter);
  317. }
  318. BreakBand(band);
  319. return;
  320. }
  321. else
  322. {
  323. EndColumn();
  324. band.FlagMustBreak = true;
  325. AddToPreparedPages(band);
  326. band.FlagMustBreak = false;
  327. return;
  328. }
  329. }
  330. else
  331. {
  332. // since we don't show the column footer band in the EndLastPage, do it here.
  333. if (isReportSummary)
  334. {
  335. if ((band as ReportSummaryBand).KeepWithData)
  336. {
  337. EndKeep();
  338. }
  339. ShowReprintFooters(false);
  340. ShowBand(page.ColumnFooter);
  341. }
  342. }
  343. // check if we have a child band with FillUnusedSpace flag
  344. if (band.Child != null && band.Child.FillUnusedSpace)
  345. {
  346. // if we reprint a data/group footer, do not include the band height into calculation:
  347. // it is already counted in FreeSpace
  348. float bandHeight = band.Height;
  349. if (band.Repeated)
  350. {
  351. bandHeight = 0;
  352. }
  353. while (FreeSpace - bandHeight - band.Child.Height >= 0)
  354. {
  355. float saveCurY = CurY;
  356. ShowBand(band.Child);
  357. // nothing was printed, break to avoid an endless loop
  358. if (CurY == saveCurY)
  359. {
  360. break;
  361. }
  362. }
  363. }
  364. // adjust the band location
  365. if (band is PageFooterBand && !UnlimitedHeight)
  366. {
  367. CurY = PageHeight - GetBandHeightWithChildren(band);
  368. }
  369. if (!isPageBand)
  370. {
  371. band.Left += originX + CurX;
  372. }
  373. if (band.PrintOnBottom)
  374. {
  375. CurY = PageHeight - PageFooterHeight - ColumnFooterHeight;
  376. // if PrintOnBottom is applied to a band like DataFooter, print it with all its child bands
  377. // if PrintOnBottom is applied to a child band, print this band only.
  378. if (band is ChildBand)
  379. {
  380. CurY -= band.Height;
  381. }
  382. else
  383. {
  384. CurY -= GetBandHeightWithChildren(band);
  385. }
  386. }
  387. band.Top = CurY;
  388. // shift the band and decrease its width when printing hierarchy
  389. float saveLeft = band.Left;
  390. float saveWidth = band.Width;
  391. if (!isPageBand && !isColumnBand)
  392. {
  393. band.Left += hierarchyIndent;
  394. band.Width -= hierarchyIndent;
  395. }
  396. // add outline
  397. AddBandOutline(band);
  398. // add bookmarks
  399. band.AddBookmarks();
  400. // put the band to prepared pages. Do not put page bands twice
  401. // (this may happen when we render a subreport, or append a report to another one).
  402. bool bandAdded = true;
  403. bool bandAlreadyExists = false;
  404. if (isPageBand)
  405. {
  406. if (band is ChildBand)
  407. {
  408. bandAlreadyExists = PreparedPages.ContainsBand(band.Name);
  409. }
  410. else
  411. {
  412. bandAlreadyExists = PreparedPages.ContainsBand(band.GetType());
  413. }
  414. }
  415. if (!bandAlreadyExists)
  416. {
  417. bandAdded = PreparedPages.AddBand(band);
  418. }
  419. // shift CurY
  420. if (bandAdded && !(mainBand is OverlayBand))
  421. {
  422. CurY += band.Height;
  423. }
  424. // set left&width back
  425. band.Left = saveLeft;
  426. band.Width = saveWidth;
  427. }
  428. #endregion Internal Methods
  429. }
  430. }