123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- using FastReport.Data;
- using FastReport.Preview;
- using FastReport.Utils;
- using System;
- using System.Collections.Generic;
- namespace FastReport.Engine
- {
- /// <summary>
- /// Represents the report engine.
- /// </summary>
- public partial class ReportEngine
- {
- #region Fields
- private Report report;
- private float curX;
- private float originX;
- private float curY;
- private int curColumn;
- private BandBase curBand;
- private DateTime date;
- private int hierarchyLevel;
- private float hierarchyIndent;
- private string hierarchyRowNo;
- private bool finalPass;
- private int firstReportPage;
- private bool isFirstReportPage;
- private int pagesLimit = 0;
- #endregion Fields
- #region Properties
- private Report Report
- {
- get { return report; }
- }
- private PreparedPages PreparedPages
- {
- get { return report.PreparedPages; }
- }
- /// <summary>
- /// Gets or sets the current X offset.
- /// </summary>
- /// <remarks>
- /// This property specifies the X offset where the current band will be printed.
- /// </remarks>
- public float CurX
- {
- get { return curX; }
- set { curX = Converter.DecreasePrecision(value, 2); }
- }
- /// <summary>
- /// Gets or sets the current Y offset.
- /// </summary>
- /// <remarks>
- /// This property specifies the Y offset where the current band will be printed.
- /// After the band is printed, this value is incremented by the band's height.
- /// </remarks>
- public float CurY
- {
- get { return curY; }
- set { curY = Converter.DecreasePrecision(value, 2); }
- }
- /// <summary>
- /// Gets the index of currently printing column in the multi-column report.
- /// </summary>
- /// <remarks>
- /// This value is 0-based.
- /// </remarks>
- public int CurColumn
- {
- get { return curColumn; }
- }
- /// <summary>
- /// Gets or sets index of current prepared page the current band will print on.
- /// </summary>
- /// <remarks>
- /// Note: the page with specified index must exists. This property is used to print side-by-side
- /// subreports and Table object. Usually you don't need to use it.
- /// </remarks>
- public int CurPage
- {
- get { return PreparedPages.CurPage; }
- set { PreparedPages.CurPage = value; }
- }
- /// <summary>
- /// Gets the current page width, in pixels.
- /// </summary>
- /// <remarks>
- /// This property returns a paper width minus left and right margins.
- /// </remarks>
- public float PageWidth
- {
- get { return page.WidthInPixels - (page.LeftMargin + page.RightMargin) * Units.Millimeters; }
- }
- /// <summary>
- /// Gets the current page height, in pixels.
- /// </summary>
- /// <remarks>
- /// This property returns a paper height minus top and bottom margins.
- /// </remarks>
- public float PageHeight
- {
- get { return page.HeightInPixels - (page.TopMargin + page.BottomMargin) * Units.Millimeters; }
- }
- /// <summary>
- /// Gets the value indicating whether the page has unlimited height.
- /// </summary>
- public bool UnlimitedHeight
- {
- get { return page.UnlimitedHeight; }
- }
- /// <summary>
- /// Gets the value indicating whether the page has unlimited width.
- /// </summary>
- public bool UnlimitedWidth
- {
- get { return page.UnlimitedWidth; }
- }
- /// <summary>
- /// Gets or sets the current height of unlimited page.
- /// </summary>
- public float UnlimitedHeightValue
- {
- get { return page.UnlimitedHeightValue; }
- set { page.UnlimitedHeightValue = value; }
- }
- /// <summary>
- /// Gets or sets the current width of unlimited page.
- /// </summary>
- public float UnlimitedWidthValue
- {
- get { return page.UnlimitedWidthValue; }
- set { page.UnlimitedWidthValue = value; }
- }
- /// <summary>
- /// Gets the height of page footer (including all its child bands), in pixels.
- /// </summary>
- public float PageFooterHeight
- {
- get { return GetBandHeightWithChildren(page.PageFooter); }
- }
- /// <summary>
- /// Gets the height of column footer (including all its child bands), in pixels.
- /// </summary>
- public float ColumnFooterHeight
- {
- get { return GetBandHeightWithChildren(page.ColumnFooter); }
- }
- /// <summary>
- /// Gets the free space on the current page, in pixels.
- /// </summary>
- /// <remarks>
- /// This property returns the page height minus footers height minus <b>CurY</b> value.
- /// </remarks>
- public float FreeSpace
- {
- get
- {
- float pageHeight = PageHeight;
- pageHeight -= PageFooterHeight;
- pageHeight -= ColumnFooterHeight;
- pageHeight -= GetFootersHeight();
- return Converter.DecreasePrecision(pageHeight - CurY, 2);
- }
- }
- /// <summary>
- /// Gets the current prepared page number.
- /// </summary>
- /// <remarks>
- /// This value is 1-based. The initial value (usually 1) is set in the Report.InitialPageNumber property.
- /// </remarks>
- public int PageNo
- {
- get { return GetLogicalPageNumber(); }
- }
- /// <summary>
- /// Gets the number of total pages in a prepared report.
- /// </summary>
- /// <remarks>
- /// To use this property, your report must be two-pass. Set the <see cref="FastReport.Report.DoublePass"/>
- /// property to <b>true</b>.
- /// </remarks>
- public int TotalPages
- {
- get { return GetLogicalTotalPages(); }
- }
- /// <summary>
- /// Gets the string that represents the current page number.
- /// </summary>
- /// <remarks>
- /// This property returns a locale-based value, for example: "Page 1".
- /// </remarks>
- public string PageN
- {
- get { return String.Format(Res.Get("Misc,PageN"), PageNo); }
- }
- /// <summary>
- /// Gets the string that represents the "Page N of M" number.
- /// </summary>
- /// <remarks>
- /// This property returns a locale-based value, for example: "Page 1 of 10".
- /// </remarks>
- public string PageNofM
- {
- get { return String.Format(Res.Get("Misc,PageNofM"), PageNo, TotalPages); }
- }
- /// <summary>
- /// Gets the current row number of currently printing band.
- /// </summary>
- /// <remarks>
- /// This value is 1-based. It resets to 1 on each new group.
- /// </remarks>
- public int RowNo
- {
- get { return curBand == null ? 0 : curBand.RowNo; }
- }
- /// <summary>
- /// Gets the running current row number of currently printing band.
- /// </summary>
- /// <remarks>
- /// This value is 1-based.
- /// </remarks>
- public int AbsRowNo
- {
- get { return curBand == null ? 0 : curBand.AbsRowNo; }
- }
- /// <summary>
- /// Gets the date of report start.
- /// </summary>
- public DateTime Date
- {
- get { return date; }
- }
- /// <summary>
- /// Gets a value indicating whether the report is executing the final pass.
- /// </summary>
- /// <remarks>
- /// This property is <b>true</b> if report is one-pass, or if report is two-pass and
- /// the second pass is executing.
- /// </remarks>
- public bool FinalPass
- {
- get { return finalPass; }
- }
- /// <summary>
- /// Gets a value indicating whether the report is executing the first pass.
- /// </summary>
- /// <remarks>
- /// This property is <b>true</b> if report is one-pass, or if report is two-pass and
- /// the first pass is executing.
- /// </remarks>
- public bool FirstPass
- {
- get { return !(Report.DoublePass && FinalPass); }
- }
- /// <summary>
- /// Gets a level of hierarchy when printing hierarchical bands.
- /// </summary>
- /// <remarks>
- /// The first level of hierarchy has 0 index.
- /// </remarks>
- public int HierarchyLevel
- {
- get { return hierarchyLevel; }
- }
- /// <summary>
- /// Gets the row number like "1.2.1" when printing hierarchical bands.
- /// </summary>
- public string HierarchyRowNo
- {
- get { return hierarchyRowNo; }
- }
- #endregion Properties
- #region Constructors
- internal ReportEngine(Report report)
- {
- this.report = report;
- objectsToProcess = new List<ProcessInfo>();
- }
- #endregion Constructors
- #region Private Methods
- private void ResetDesigningFlag()
- {
- ObjectCollection allObjects = Report.AllObjects;
- foreach (Base c in allObjects)
- {
- c.SetDesigning(false);
- }
- }
- private void InitializeData()
- {
- foreach (Base c in Report.Dictionary.AllObjects)
- {
- if (c is DataComponentBase)
- (c as DataComponentBase).InitializeComponent();
- }
- foreach (Base c in Report.AllObjects)
- {
- if (c is ReportComponentBase obj)
- obj.ResetData();
- }
- }
- private void InitializeSecondPassData()
- {
- foreach (Base c in Report.Dictionary.AllObjects)
- {
- if (c is DataSourceBase)
- {
- DataSourceBase data = c as DataSourceBase;
- if (data.RowCount > 0)
- data.First();
- }
- }
- }
- private void PrepareToFirstPass(bool append)
- {
- finalPass = !Report.DoublePass;
- if (!append)
- PreparedPages.Clear();
- else
- PreparedPages.CurPage = PreparedPages.Count > 0 ? PreparedPages.Count - 1 : 0;
- isFirstReportPage = true;
- hierarchyLevel = 1;
- PreparedPages.PrepareToFirstPass();
- Report.Dictionary.Totals.ClearValues();
- objectsToProcess.Clear();
- InitializePages();
- InitPageNumbers();
- }
- private void PrepareToSecondPass()
- {
- Report.Dictionary.Totals.ClearValues();
- objectsToProcess.Clear();
- PreparedPages.ClearFirstPass();
- InitializeSecondPassData();
- }
- private float GetBandHeightWithChildren(BandBase band)
- {
- float result = 0;
- while (band != null)
- {
- if (CanPrint(band))
- result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
- else if (FinalPass && !String.IsNullOrEmpty(band.VisibleExpression) && band.VisibleExpression.Contains("TotalPages"))
- result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
- band = band.Child;
- if (band != null && ((band as ChildBand).FillUnusedSpace || (band as ChildBand).CompleteToNRows != 0))
- break;
- }
- return result;
- }
- private void RunReportPages(ReportPage page)
- {
- OnStateChanged(Report, EngineState.ReportStarted);
- if (page == null)
- RunReportPages();
- else
- RunReportPage(page);
- OnStateChanged(Report, EngineState.ReportFinished);
- }
- private void LimitPreparedPages()
- {
- // limit the prepared pages
- if (Report.MaxPages > 0)
- {
- while (PreparedPages.Count > Report.MaxPages)
- {
- PreparedPages.RemovePage(PreparedPages.Count - 1);
- }
- }
- // Limit the prepared pages again because pagesLimit has better priority than Report.MaxPages.
- if (pagesLimit > 0)
- {
- while (PreparedPages.Count > pagesLimit)
- {
- PreparedPages.RemovePage(PreparedPages.Count - 1);
- }
- }
- }
- #endregion Private Methods
- #region Internal Methods
- internal bool Run(bool runDialogs, bool append, bool resetDataState)
- {
- return Run(runDialogs, append, resetDataState, null);
- }
- internal bool Run(bool runDialogs, bool append, bool resetDataState, int pagesLimit)
- {
- this.pagesLimit = pagesLimit;
- return Run(runDialogs, append, resetDataState, null);
- }
- internal bool Run(bool runDialogs, bool append, bool resetDataState, ReportPage page)
- {
- RunPhase1(resetDataState);
- try
- {
- if (runDialogs && !RunDialogs())
- return false;
- RunPhase2(append, page);
- }
- finally
- {
- RunFinished();
- }
- return true;
- }
- internal void RunPhase1(bool resetDataState = true, bool webDialog = false)
- {
- date = SystemFake.DateTime.Now;
- Report.SetOperation(ReportOperation.Running);
- ResetDesigningFlag();
- // don't reset the data state if we run the hyperlink's detail page or refresh a report.
- // This is necessary to keep data filtering settings alive
- if (resetDataState)
- InitializeData();
- // don't call OnStartReport event again, if it's web dialog re-render
- if (!webDialog)
- Report.OnStartReport(EventArgs.Empty);
- }
- internal void RunPhase2(bool append, ReportPage page)
- {
- Config.ReportSettings.OnStartProgress(Report);
- PrepareToFirstPass(append);
- RunReportPages(page);
-
- ResetLogicalPageNumber();
-
- if (Report.DoublePass && !Report.Aborted)
- {
- finalPass = true;
- PrepareToSecondPass();
- RunReportPages(page);
- }
- }
- internal void RunPhase2(int? pagesLimit = null)
- {
- if (pagesLimit != null)
- this.pagesLimit = pagesLimit.Value;
- try
- {
- RunPhase2(false, null);
- }
- finally
- {
- RunFinished();
- }
- }
- internal void RunFinished()
- {
- Report.OnFinishReport(EventArgs.Empty);
- Config.ReportSettings.OnFinishProgress(Report);
- Report.SetOperation(ReportOperation.None);
- LimitPreparedPages();
- }
- #endregion Internal Methods
- }
- }
|