ReportEngine.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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. }
  296. private void InitializeSecondPassData()
  297. {
  298. foreach (Base c in Report.Dictionary.AllObjects)
  299. {
  300. if (c is DataSourceBase)
  301. {
  302. DataSourceBase data = c as DataSourceBase;
  303. if (data.RowCount > 0)
  304. data.First();
  305. }
  306. }
  307. }
  308. private void PrepareToFirstPass(bool append)
  309. {
  310. finalPass = !Report.DoublePass;
  311. if (!append)
  312. PreparedPages.Clear();
  313. else
  314. PreparedPages.CurPage = PreparedPages.Count > 0 ? PreparedPages.Count - 1 : 0;
  315. isFirstReportPage = true;
  316. hierarchyLevel = 1;
  317. PreparedPages.PrepareToFirstPass();
  318. Report.Dictionary.Totals.ClearValues();
  319. objectsToProcess.Clear();
  320. InitializePages();
  321. InitPageNumbers();
  322. }
  323. private void PrepareToSecondPass()
  324. {
  325. Report.Dictionary.Totals.ClearValues();
  326. objectsToProcess.Clear();
  327. PreparedPages.ClearFirstPass();
  328. InitializeSecondPassData();
  329. }
  330. private float GetBandHeightWithChildren(BandBase band)
  331. {
  332. float result = 0;
  333. while (band != null)
  334. {
  335. if (CanPrint(band))
  336. result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
  337. else if (FinalPass && !String.IsNullOrEmpty(band.VisibleExpression) && band.VisibleExpression.Contains("TotalPages"))
  338. result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
  339. band = band.Child;
  340. if (band != null && ((band as ChildBand).FillUnusedSpace || (band as ChildBand).CompleteToNRows != 0))
  341. break;
  342. }
  343. return result;
  344. }
  345. private void RunReportPages(ReportPage page)
  346. {
  347. OnStateChanged(Report, EngineState.ReportStarted);
  348. if (page == null)
  349. RunReportPages();
  350. else
  351. RunReportPage(page);
  352. OnStateChanged(Report, EngineState.ReportFinished);
  353. }
  354. #endregion Private Methods
  355. #region Internal Methods
  356. internal bool Run(bool runDialogs, bool append, bool resetDataState)
  357. {
  358. return Run(runDialogs, append, resetDataState, null);
  359. }
  360. internal bool Run(bool runDialogs, bool append, bool resetDataState, int pagesLimit)
  361. {
  362. this.pagesLimit = pagesLimit;
  363. return Run(runDialogs, append, resetDataState, null);
  364. }
  365. internal bool Run(bool runDialogs, bool append, bool resetDataState, ReportPage page)
  366. {
  367. RunPhase1(resetDataState);
  368. try
  369. {
  370. if (runDialogs && !RunDialogs())
  371. return false;
  372. Config.ReportSettings.OnStartProgress(Report);
  373. PrepareToFirstPass(append);
  374. RunReportPages(page);
  375. ResetLogicalPageNumber();
  376. if (Report.DoublePass && !Report.Aborted)
  377. {
  378. finalPass = true;
  379. PrepareToSecondPass();
  380. RunReportPages(page);
  381. }
  382. }
  383. finally
  384. {
  385. Report.OnFinishReport(EventArgs.Empty);
  386. Config.ReportSettings.OnFinishProgress(Report);
  387. Report.SetOperation(ReportOperation.None);
  388. // limit the prepared pages
  389. if (Report.MaxPages > 0)
  390. {
  391. while (PreparedPages.Count > Report.MaxPages)
  392. {
  393. PreparedPages.RemovePage(PreparedPages.Count - 1);
  394. }
  395. }
  396. // Limit the prepared pages again because pagesLimit has better priority than Report.MaxPages.
  397. if (pagesLimit > 0)
  398. {
  399. while (PreparedPages.Count > pagesLimit)
  400. {
  401. PreparedPages.RemovePage(PreparedPages.Count - 1);
  402. }
  403. }
  404. }
  405. return true;
  406. }
  407. internal void RunPhase1(bool resetDataState = true, bool webDialog = false)
  408. {
  409. date = SystemFake.DateTime.Now;
  410. Report.SetOperation(ReportOperation.Running);
  411. ResetDesigningFlag();
  412. // don't reset the data state if we run the hyperlink's detail page or refresh a report.
  413. // This is necessary to keep data filtering settings alive
  414. if (resetDataState)
  415. InitializeData();
  416. // don't call OnStartReport event again, if it's web dialog re-render
  417. if(!webDialog)
  418. Report.OnStartReport(EventArgs.Empty);
  419. }
  420. internal void RunPhase2(int? pagesLimit = null)
  421. {
  422. if (pagesLimit != null)
  423. this.pagesLimit = pagesLimit.Value;
  424. try
  425. {
  426. Config.ReportSettings.OnStartProgress(Report);
  427. PrepareToFirstPass(false);
  428. RunReportPages();
  429. ResetLogicalPageNumber();
  430. if (Report.DoublePass && !Report.Aborted)
  431. {
  432. finalPass = true;
  433. PrepareToSecondPass();
  434. RunReportPages();
  435. }
  436. }
  437. finally
  438. {
  439. Report.OnFinishReport(EventArgs.Empty);
  440. Config.ReportSettings.OnFinishProgress(Report);
  441. Report.SetOperation(ReportOperation.None);
  442. // limit the prepared pages
  443. if (Report.MaxPages > 0)
  444. {
  445. while (PreparedPages.Count > Report.MaxPages)
  446. {
  447. PreparedPages.RemovePage(PreparedPages.Count - 1);
  448. }
  449. }
  450. // Limit the prepared pages again because pagesLimit has better priority than Report.MaxPages.
  451. if (pagesLimit > 0)
  452. {
  453. while (PreparedPages.Count > pagesLimit)
  454. {
  455. PreparedPages.RemovePage(PreparedPages.Count - 1);
  456. }
  457. }
  458. }
  459. }
  460. #endregion Internal Methods
  461. }
  462. }