ReportEngine.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. using FastReport.Data;
  2. using FastReport.Preview;
  3. using FastReport.Utils;
  4. using System;
  5. using System.Collections.Generic;
  6. namespace FastReport.Engine
  7. {
  8. /// <summary>
  9. /// Represents the report engine.
  10. /// </summary>
  11. public partial class ReportEngine
  12. {
  13. #region Fields
  14. private Report report;
  15. private float curX;
  16. private float originX;
  17. private float curY;
  18. private int curColumn;
  19. private BandBase curBand;
  20. private DateTime date;
  21. private int hierarchyLevel;
  22. private float hierarchyIndent;
  23. private string hierarchyRowNo;
  24. private bool finalPass;
  25. private int firstReportPage;
  26. private bool isFirstReportPage;
  27. private int pagesLimit = 0;
  28. #endregion Fields
  29. #region Properties
  30. private Report Report
  31. {
  32. get { return report; }
  33. }
  34. private PreparedPages PreparedPages
  35. {
  36. get { return report.PreparedPages; }
  37. }
  38. /// <summary>
  39. /// Gets or sets the current X offset.
  40. /// </summary>
  41. /// <remarks>
  42. /// This property specifies the X offset where the current band will be printed.
  43. /// </remarks>
  44. public float CurX
  45. {
  46. get { return curX; }
  47. set { curX = Converter.DecreasePrecision(value, 2); }
  48. }
  49. /// <summary>
  50. /// Gets or sets the current Y offset.
  51. /// </summary>
  52. /// <remarks>
  53. /// This property specifies the Y offset where the current band will be printed.
  54. /// After the band is printed, this value is incremented by the band's height.
  55. /// </remarks>
  56. public float CurY
  57. {
  58. get { return curY; }
  59. set { curY = Converter.DecreasePrecision(value, 2); }
  60. }
  61. /// <summary>
  62. /// Gets the index of currently printing column in the multi-column report.
  63. /// </summary>
  64. /// <remarks>
  65. /// This value is 0-based.
  66. /// </remarks>
  67. public int CurColumn
  68. {
  69. get { return curColumn; }
  70. }
  71. /// <summary>
  72. /// Gets or sets index of current prepared page the current band will print on.
  73. /// </summary>
  74. /// <remarks>
  75. /// Note: the page with specified index must exists. This property is used to print side-by-side
  76. /// subreports and Table object. Usually you don't need to use it.
  77. /// </remarks>
  78. public int CurPage
  79. {
  80. get { return PreparedPages.CurPage; }
  81. set { PreparedPages.CurPage = value; }
  82. }
  83. /// <summary>
  84. /// Gets the current page width, in pixels.
  85. /// </summary>
  86. /// <remarks>
  87. /// This property returns a paper width minus left and right margins.
  88. /// </remarks>
  89. public float PageWidth
  90. {
  91. get { return page.WidthInPixels - (page.LeftMargin + page.RightMargin) * Units.Millimeters; }
  92. }
  93. /// <summary>
  94. /// Gets the current page height, in pixels.
  95. /// </summary>
  96. /// <remarks>
  97. /// This property returns a paper height minus top and bottom margins.
  98. /// </remarks>
  99. public float PageHeight
  100. {
  101. get { return page.HeightInPixels - (page.TopMargin + page.BottomMargin) * Units.Millimeters; }
  102. }
  103. /// <summary>
  104. /// Gets the value indicating whether the page has unlimited height.
  105. /// </summary>
  106. public bool UnlimitedHeight
  107. {
  108. get { return page.UnlimitedHeight; }
  109. }
  110. /// <summary>
  111. /// Gets the value indicating whether the page has unlimited width.
  112. /// </summary>
  113. public bool UnlimitedWidth
  114. {
  115. get { return page.UnlimitedWidth; }
  116. }
  117. /// <summary>
  118. /// Gets or sets the current height of unlimited page.
  119. /// </summary>
  120. public float UnlimitedHeightValue
  121. {
  122. get { return page.UnlimitedHeightValue; }
  123. set { page.UnlimitedHeightValue = value; }
  124. }
  125. /// <summary>
  126. /// Gets or sets the current width of unlimited page.
  127. /// </summary>
  128. public float UnlimitedWidthValue
  129. {
  130. get { return page.UnlimitedWidthValue; }
  131. set { page.UnlimitedWidthValue = value; }
  132. }
  133. /// <summary>
  134. /// Gets the height of page footer (including all its child bands), in pixels.
  135. /// </summary>
  136. public float PageFooterHeight
  137. {
  138. get { return GetBandHeightWithChildren(page.PageFooter); }
  139. }
  140. /// <summary>
  141. /// Gets the height of column footer (including all its child bands), in pixels.
  142. /// </summary>
  143. public float ColumnFooterHeight
  144. {
  145. get { return GetBandHeightWithChildren(page.ColumnFooter); }
  146. }
  147. /// <summary>
  148. /// Gets the free space on the current page, in pixels.
  149. /// </summary>
  150. /// <remarks>
  151. /// This property returns the page height minus footers height minus <b>CurY</b> value.
  152. /// </remarks>
  153. public float FreeSpace
  154. {
  155. get
  156. {
  157. float pageHeight = PageHeight;
  158. pageHeight -= PageFooterHeight;
  159. pageHeight -= ColumnFooterHeight;
  160. pageHeight -= GetFootersHeight();
  161. return Converter.DecreasePrecision(pageHeight - CurY, 2);
  162. }
  163. }
  164. /// <summary>
  165. /// Gets the current prepared page number.
  166. /// </summary>
  167. /// <remarks>
  168. /// This value is 1-based. The initial value (usually 1) is set in the Report.InitialPageNumber property.
  169. /// </remarks>
  170. public int PageNo
  171. {
  172. get { return GetLogicalPageNumber(); }
  173. }
  174. /// <summary>
  175. /// Gets the number of total pages in a prepared report.
  176. /// </summary>
  177. /// <remarks>
  178. /// To use this property, your report must be two-pass. Set the <see cref="FastReport.Report.DoublePass"/>
  179. /// property to <b>true</b>.
  180. /// </remarks>
  181. public int TotalPages
  182. {
  183. get { return GetLogicalTotalPages(); }
  184. }
  185. /// <summary>
  186. /// Gets the string that represents the current page number.
  187. /// </summary>
  188. /// <remarks>
  189. /// This property returns a locale-based value, for example: "Page 1".
  190. /// </remarks>
  191. public string PageN
  192. {
  193. get { return String.Format(Res.Get("Misc,PageN"), PageNo); }
  194. }
  195. /// <summary>
  196. /// Gets the string that represents the "Page N of M" number.
  197. /// </summary>
  198. /// <remarks>
  199. /// This property returns a locale-based value, for example: "Page 1 of 10".
  200. /// </remarks>
  201. public string PageNofM
  202. {
  203. get { return String.Format(Res.Get("Misc,PageNofM"), PageNo, TotalPages); }
  204. }
  205. /// <summary>
  206. /// Gets the current row number of currently printing band.
  207. /// </summary>
  208. /// <remarks>
  209. /// This value is 1-based. It resets to 1 on each new group.
  210. /// </remarks>
  211. public int RowNo
  212. {
  213. get { return curBand == null ? 0 : curBand.RowNo; }
  214. }
  215. /// <summary>
  216. /// Gets the running current row number of currently printing band.
  217. /// </summary>
  218. /// <remarks>
  219. /// This value is 1-based.
  220. /// </remarks>
  221. public int AbsRowNo
  222. {
  223. get { return curBand == null ? 0 : curBand.AbsRowNo; }
  224. }
  225. /// <summary>
  226. /// Gets the date of report start.
  227. /// </summary>
  228. public DateTime Date
  229. {
  230. get { return date; }
  231. }
  232. /// <summary>
  233. /// Gets a value indicating whether the report is executing the final pass.
  234. /// </summary>
  235. /// <remarks>
  236. /// This property is <b>true</b> if report is one-pass, or if report is two-pass and
  237. /// the second pass is executing.
  238. /// </remarks>
  239. public bool FinalPass
  240. {
  241. get { return finalPass; }
  242. }
  243. /// <summary>
  244. /// Gets a value indicating whether the report is executing the first pass.
  245. /// </summary>
  246. /// <remarks>
  247. /// This property is <b>true</b> if report is one-pass, or if report is two-pass and
  248. /// the first pass is executing.
  249. /// </remarks>
  250. public bool FirstPass
  251. {
  252. get { return !(Report.DoublePass && FinalPass); }
  253. }
  254. /// <summary>
  255. /// Gets a level of hierarchy when printing hierarchical bands.
  256. /// </summary>
  257. /// <remarks>
  258. /// The first level of hierarchy has 0 index.
  259. /// </remarks>
  260. public int HierarchyLevel
  261. {
  262. get { return hierarchyLevel; }
  263. }
  264. /// <summary>
  265. /// Gets the row number like "1.2.1" when printing hierarchical bands.
  266. /// </summary>
  267. public string HierarchyRowNo
  268. {
  269. get { return hierarchyRowNo; }
  270. }
  271. #endregion Properties
  272. #region Constructors
  273. internal ReportEngine(Report report)
  274. {
  275. this.report = report;
  276. objectsToProcess = new List<ProcessInfo>();
  277. }
  278. #endregion Constructors
  279. #region Private Methods
  280. private void ResetDesigningFlag()
  281. {
  282. ObjectCollection allObjects = Report.AllObjects;
  283. foreach (Base c in allObjects)
  284. {
  285. c.SetDesigning(false);
  286. }
  287. }
  288. private void InitializeData()
  289. {
  290. foreach (Base c in Report.Dictionary.AllObjects)
  291. {
  292. if (c is DataComponentBase)
  293. (c as DataComponentBase).InitializeComponent();
  294. }
  295. foreach (Base c in Report.AllObjects)
  296. {
  297. if (c is ReportComponentBase obj)
  298. obj.ResetData();
  299. }
  300. }
  301. private void InitializeSecondPassData()
  302. {
  303. foreach (Base c in Report.Dictionary.AllObjects)
  304. {
  305. if (c is DataSourceBase)
  306. {
  307. DataSourceBase data = c as DataSourceBase;
  308. if (data.RowCount > 0)
  309. data.First();
  310. }
  311. }
  312. }
  313. private void PrepareToFirstPass(bool append)
  314. {
  315. finalPass = !Report.DoublePass;
  316. if (!append)
  317. PreparedPages.Clear();
  318. else
  319. PreparedPages.CurPage = PreparedPages.Count > 0 ? PreparedPages.Count - 1 : 0;
  320. isFirstReportPage = true;
  321. hierarchyLevel = 1;
  322. PreparedPages.PrepareToFirstPass();
  323. Report.Dictionary.Totals.ClearValues();
  324. objectsToProcess.Clear();
  325. InitializePages();
  326. InitPageNumbers();
  327. }
  328. private void PrepareToSecondPass()
  329. {
  330. Report.Dictionary.Totals.ClearValues();
  331. objectsToProcess.Clear();
  332. PreparedPages.ClearFirstPass();
  333. InitializeSecondPassData();
  334. }
  335. private float GetBandHeightWithChildren(BandBase band)
  336. {
  337. float result = 0;
  338. while (band != null)
  339. {
  340. if (CanPrint(band))
  341. result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
  342. else if (FinalPass && !String.IsNullOrEmpty(band.VisibleExpression) && band.VisibleExpression.Contains("TotalPages"))
  343. result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
  344. band = band.Child;
  345. if (band != null && ((band as ChildBand).FillUnusedSpace || (band as ChildBand).CompleteToNRows != 0))
  346. break;
  347. }
  348. return result;
  349. }
  350. private void RunReportPages(ReportPage page)
  351. {
  352. OnStateChanged(Report, EngineState.ReportStarted);
  353. if (page == null)
  354. RunReportPages();
  355. else
  356. RunReportPage(page);
  357. OnStateChanged(Report, EngineState.ReportFinished);
  358. }
  359. private void LimitPreparedPages()
  360. {
  361. // limit the prepared pages
  362. if (Report.MaxPages > 0)
  363. {
  364. while (PreparedPages.Count > Report.MaxPages)
  365. {
  366. PreparedPages.RemovePage(PreparedPages.Count - 1);
  367. }
  368. }
  369. // Limit the prepared pages again because pagesLimit has better priority than Report.MaxPages.
  370. if (pagesLimit > 0)
  371. {
  372. while (PreparedPages.Count > pagesLimit)
  373. {
  374. PreparedPages.RemovePage(PreparedPages.Count - 1);
  375. }
  376. }
  377. }
  378. #endregion Private Methods
  379. #region Internal Methods
  380. internal bool Run(bool runDialogs, bool append, bool resetDataState)
  381. {
  382. return Run(runDialogs, append, resetDataState, null);
  383. }
  384. internal bool Run(bool runDialogs, bool append, bool resetDataState, int pagesLimit)
  385. {
  386. this.pagesLimit = pagesLimit;
  387. return Run(runDialogs, append, resetDataState, null);
  388. }
  389. internal bool Run(bool runDialogs, bool append, bool resetDataState, ReportPage page)
  390. {
  391. RunPhase1(resetDataState);
  392. try
  393. {
  394. if (runDialogs && !RunDialogs())
  395. return false;
  396. RunPhase2(append, page);
  397. }
  398. finally
  399. {
  400. RunFinished();
  401. }
  402. return true;
  403. }
  404. internal void RunPhase1(bool resetDataState = true, bool webDialog = false)
  405. {
  406. date = SystemFake.DateTime.Now;
  407. Report.SetOperation(ReportOperation.Running);
  408. ResetDesigningFlag();
  409. // don't reset the data state if we run the hyperlink's detail page or refresh a report.
  410. // This is necessary to keep data filtering settings alive
  411. if (resetDataState)
  412. InitializeData();
  413. // don't call OnStartReport event again, if it's web dialog re-render
  414. if (!webDialog)
  415. Report.OnStartReport(EventArgs.Empty);
  416. }
  417. internal void RunPhase2(bool append, ReportPage page)
  418. {
  419. Config.ReportSettings.OnStartProgress(Report);
  420. PrepareToFirstPass(append);
  421. RunReportPages(page);
  422. ResetLogicalPageNumber();
  423. if (Report.DoublePass && !Report.Aborted)
  424. {
  425. finalPass = true;
  426. PrepareToSecondPass();
  427. RunReportPages(page);
  428. }
  429. }
  430. internal void RunPhase2(int? pagesLimit = null)
  431. {
  432. if (pagesLimit != null)
  433. this.pagesLimit = pagesLimit.Value;
  434. try
  435. {
  436. RunPhase2(false, null);
  437. }
  438. finally
  439. {
  440. RunFinished();
  441. }
  442. }
  443. internal void RunFinished()
  444. {
  445. Report.OnFinishReport(EventArgs.Empty);
  446. Config.ReportSettings.OnFinishProgress(Report);
  447. Report.SetOperation(ReportOperation.None);
  448. LimitPreparedPages();
  449. }
  450. #endregion Internal Methods
  451. }
  452. }