123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- using FastReport.Data;
- using FastReport.Utils;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- namespace FastReport.Engine
- {
- public partial class ReportEngine
- {
- #region Private Classes
- private class HierarchyItem
- {
- #region Fields
- public int rowNo;
- public List<HierarchyItem> items;
- public HierarchyItem parent;
- #endregion Fields
- #region Constructors
- public HierarchyItem()
- {
- items = new List<HierarchyItem>();
- }
- #endregion Constructors
- #region Public Methods
- public void Add(HierarchyItem item)
- {
- items.Add(item);
- item.parent = this;
- }
- #endregion Public Methods
- }
- #endregion Private Classes
- #region Private Methods
- private void RunDataBand(DataBand dataBand)
- {
- if (page.Columns.Count > 1 && Report.Engine.UnlimitedHeight)
- dataBand.Columns.Count = page.Columns.Count;
- dataBand.InitDataSource();
- dataBand.DataSource.First();
- int rowCount = dataBand.DataSource.RowCount;
- if (dataBand.IsDatasourceEmpty && dataBand.PrintIfDatasourceEmpty)
- rowCount = 1;
- if (dataBand.CollectChildRows && rowCount > 1)
- rowCount = 1;
- if (dataBand.MaxRows > 0 && rowCount > dataBand.MaxRows)
- rowCount = dataBand.MaxRows;
- bool keepFirstRow = NeedKeepFirstRow(dataBand);
- bool keepLastRow = NeedKeepLastRow(dataBand);
- RunDataBand(dataBand, rowCount, keepFirstRow, keepLastRow);
- // do not leave the datasource in EOF state to allow print something in the footer
- dataBand.DataSource.Prior();
- }
- private void RunDataBand(DataBand dataBand, int rowCount, bool keepFirstRow, bool keepLastRow)
- {
- if (dataBand.IsHierarchical)
- {
- ShowHierarchy(dataBand, rowCount);
- return;
- }
- bool isFirstRow = true;
- bool someRowsPrinted = false;
- dataBand.RowNo = 0;
- dataBand.IsFirstRow = false;
- dataBand.IsLastRow = false;
- // check if we have only one data row that should be kept with both header and footer
- bool oneRow = rowCount == 1 && keepFirstRow && keepLastRow;
- // cycle through records
- for (int i = 0; i < rowCount; i++)
- {
- bool isLastRow = i == rowCount - 1;
- if (!dataBand.IsDetailEmpty())
- {
- dataBand.RowNo++;
- dataBand.AbsRowNo++;
- dataBand.IsFirstRow = isFirstRow;
- dataBand.IsLastRow = isLastRow;
- // keep header
- if (isFirstRow && keepFirstRow)
- StartKeep(dataBand);
- // keep together
- if (isFirstRow && dataBand.KeepTogether)
- StartKeep(dataBand);
- // keep detail
- if (dataBand.KeepDetail)
- StartKeep(dataBand);
- // show header
- if (isFirstRow)
- ShowDataHeader(dataBand);
- // keep footer
- if (isLastRow && keepLastRow && dataBand.IsDeepmostDataBand)
- StartKeep(dataBand);
- // start block event
- if (isFirstRow)
- OnStateChanged(dataBand, EngineState.BlockStarted);
- // show band
- ShowDataBand(dataBand, rowCount);
- // end keep header
- if (isFirstRow && keepFirstRow && !oneRow)
- EndKeep();
- // end keep footer
- if (isLastRow && keepLastRow && dataBand.IsDeepmostDataBand)
- CheckKeepFooter(dataBand);
- // show sub-bands
- RunBands(dataBand.Bands);
- // up the outline
- OutlineUp(dataBand);
- // end keep detail
- if (dataBand.KeepDetail)
- EndKeep();
- isFirstRow = false;
- someRowsPrinted = true;
- if (dataBand.Columns.Count > 1)
- break;
- }
- dataBand.DataSource.Next();
- if (Report.Aborted)
- break;
- }
- // complete upto N rows
- ChildBand child = dataBand.Child;
- if (child != null && child.CompleteToNRows > rowCount)
- {
- for (int i = 0; i < child.CompleteToNRows - rowCount; i++)
- {
- child.RowNo = rowCount + i + 1;
- child.AbsRowNo = rowCount + i + 1;
- ShowBand(child);
- }
- }
- // print child if databand is empty
- if (child != null && child.PrintIfDatabandEmpty && dataBand.IsDatasourceEmpty)
- {
- ShowBand(child);
- }
- if (someRowsPrinted)
- {
- // finish block event
- OnStateChanged(dataBand, EngineState.BlockFinished);
- // show footer
- ShowDataFooter(dataBand);
- // end KeepTogether
- if (dataBand.KeepTogether)
- EndKeep();
- // end KeepLastRow
- if (keepLastRow)
- EndKeep();
- }
- }
- private void ShowDataBand(DataBand dataBand, int rowCount)
- {
- if (dataBand.Columns.Count > 1)
- {
- dataBand.Width = dataBand.Columns.ActualWidth;
- RenderMultiColumnBand(dataBand, rowCount);
- }
- else
- {
- if (dataBand.ResetPageNumber && (dataBand.FirstRowStartsNewPage || dataBand.RowNo > 1))
- ResetLogicalPageNumber();
- if (dataBand.Footer != null && dataBand.CanBreak)
- if (dataBand.Footer.KeepWithData && dataBand.Footer.Height + dataBand.Height > FreeSpace)
- {
- dataBand.AddLastToFooter(dataBand.Footer);
- }
- ShowBand(dataBand);
- }
- }
- private void RenderMultiColumnBand(DataBand dataBand, int rowCount)
- {
- if (dataBand.Columns.Layout == ColumnLayout.AcrossThenDown)
- RenderBandAcrossThenDown(dataBand, rowCount);
- else
- {
- DataSourceBase dataSource = dataBand.DataSource;
- int saveRow = dataSource.CurrentRowNo;
- // calc height of each data row. This list is shared across RenderBandDownThenAcross calls.
- Hashtable heights = new Hashtable();
- for (int i = 0; i < rowCount; i++)
- {
- dataSource.CurrentRowNo = i + saveRow;
- heights[i + saveRow] = CalcHeight(dataBand);
- }
- dataSource.CurrentRowNo = saveRow;
- while (rowCount > 0)
- {
- rowCount = RenderBandDownThenAcross(dataBand, rowCount, heights);
- }
- }
- }
- private void RenderBandAcrossThenDown(DataBand dataBand, int rowCount)
- {
- DataSourceBase dataSource = dataBand.DataSource;
- int saveRow = dataSource.CurrentRowNo;
- bool keepLastRow = NeedKeepLastRow(dataBand);
- // create output band
- using (DataBand outputBand = new DataBand())
- {
- outputBand.SetReport(Report);
- int columnNo = 0;
- for (int i = 0; i < rowCount; i++)
- {
- bool isLastRow = i == rowCount - 1;
- dataSource.CurrentRowNo = i + saveRow;
- if (columnNo == 0)
- {
- outputBand.Clear();
- outputBand.Assign(dataBand);
- outputBand.OutlineExpression = "";
- outputBand.Border = new Border();
- outputBand.Fill = new SolidFill();
- }
- // write to the output band
- if (Config.RightToLeft)
- {
- ShowBand(dataBand, outputBand, dataBand.Columns.Positions[dataBand.Columns.Count - columnNo - 1], 0);
- }
- else
- {
- ShowBand(dataBand, outputBand, dataBand.Columns.Positions[columnNo], 0);
- }
- // add outline
- AddBandOutline(dataBand);
- // outline up
- OutlineUp(dataBand);
- dataBand.RowNo++;
- dataBand.AbsRowNo++;
- columnNo++;
- if (columnNo == dataBand.Columns.Count || isLastRow)
- {
- columnNo = 0;
- // keep footer
- if (isLastRow && keepLastRow && dataBand.IsDeepmostDataBand)
- StartKeep(outputBand);
- // show output band itself
- ShowBand(outputBand, false);
- // end keep footer
- if (isLastRow && keepLastRow && dataBand.IsDeepmostDataBand)
- CheckKeepFooter(dataBand);
- }
- }
- }
- dataSource.CurrentRowNo = saveRow + rowCount;
- }
- private int RenderBandDownThenAcross(DataBand dataBand, int rowCount, Hashtable heights)
- {
- DataSourceBase dataSource = dataBand.DataSource;
- int saveRow = dataSource.CurrentRowNo;
- // determine number of rows in a column. Do not take the height into account - it's too complex.
- int rowsPerColumn = (int)Math.Ceiling((float)rowCount / dataBand.Columns.Count);
- if (rowsPerColumn < dataBand.Columns.MinRowCount)
- rowsPerColumn = dataBand.Columns.MinRowCount;
- // calculate max height of all columns to check the free space
- float maxHeight = 0;
- for (int i = 0; i < dataBand.Columns.Count; i++)
- {
- // calculate column height
- float columnHeight = 0;
- for (int j = 0; j < rowsPerColumn; j++)
- {
- int rowIndex = j + i * rowsPerColumn + saveRow;
- if (heights[rowIndex] == null)
- break;
- columnHeight += (float)heights[rowIndex];
- }
- if (columnHeight > maxHeight)
- maxHeight = columnHeight;
- }
- float saveCurX = CurX;
- float startColumnY = CurY;
- int columnNo = 0;
- if (maxHeight > FreeSpace)
- {
- // not enough free space. Render rows down then across. After finishing the page,
- // run this method again to render remaining rows.
- for (int i = 0; i < rowCount; i++)
- {
- dataSource.CurrentRowNo = i + saveRow;
- if (Config.RightToLeft)
- {
- CurX = dataBand.Columns.Positions[dataBand.Columns.Count - columnNo - 1] + saveCurX;
- }
- else
- {
- CurX = dataBand.Columns.Positions[columnNo] + saveCurX;
- }
- // check free space.
- if ((float)heights[i + saveRow] > FreeSpace && i != 0)
- {
- columnNo++;
- if (columnNo == dataBand.Columns.Count)
- {
- // start the new page
- columnNo = 0;
- CurX = saveCurX;
- EndPage();
- // decrease number of available rows and call this method again
- rowCount -= i;
- return rowCount;
- }
- else
- {
- // start the new column
- CurY = startColumnY;
- }
- // check free space again before show a band - we may be at a page end
- i--;
- continue;
- }
- if (Config.RightToLeft)
- {
- CurX = dataBand.Columns.Positions[dataBand.Columns.Count - columnNo - 1] + saveCurX;
- }
- else
- {
- CurX = dataBand.Columns.Positions[columnNo] + saveCurX;
- }
- // show a band
- ShowBand(dataBand);
- dataBand.RowNo++;
- dataBand.AbsRowNo++;
- }
- // we shouldn't go here...
- }
- else
- {
- // we have enough space to render all rows.
- float maxY = CurY;
- int rowNo = 0;
- bool carryoutLastRow = false;
- if (dataBand.IsDeepmostDataBand && NeedKeepLastRow(dataBand))
- {
- float footersHeight = GetFootersHeight(dataBand);
- if (footersHeight > FreeSpace - maxHeight)
- carryoutLastRow = true;
- }
- for (int i = 0; i < rowCount; i++)
- {
- dataSource.CurrentRowNo = i + saveRow;
- // carry out the last row
- bool isLastRow = i == rowCount - 1;
- if (isLastRow && carryoutLastRow)
- {
- CurX = saveCurX;
- CurY = maxY;
- EndColumn();
- ShowBand(dataBand);
- OutlineUp(dataBand);
- dataSource.CurrentRowNo = saveRow + rowCount;
- return 0;
- }
- if (Config.RightToLeft)
- {
- CurX = dataBand.Columns.Positions[dataBand.Columns.Count - columnNo - 1] + saveCurX;
- }
- else
- {
- CurX = dataBand.Columns.Positions[columnNo] + saveCurX;
- }
- // show a band
- ShowBand(dataBand);
- // outline up
- OutlineUp(dataBand);
- if (CurY > maxY)
- maxY = CurY;
- dataBand.RowNo++;
- dataBand.AbsRowNo++;
- rowNo++;
- // check if we need to start a new column
- if (rowNo >= rowsPerColumn)
- {
- columnNo++;
- rowNo = 0;
- CurY = startColumnY;
- }
- }
- CurX = saveCurX;
- CurY = maxY;
- }
- dataSource.CurrentRowNo = saveRow + rowCount;
- return 0;
- }
- private float GetColumnHeight(Hashtable heights, int startIndex, int rowCount, int rowNo)
- {
- float result = 0;
- for (int i = 0; i < rowCount; i++)
- {
- if (i + startIndex >= heights.Count)
- break;
- result += (float)heights[i + startIndex + rowNo];
- }
- return result;
- }
- private HierarchyItem MakeHierarchy(DataBand dataBand, int rowCount)
- {
- Dictionary<string, HierarchyItem> items = new Dictionary<string, HierarchyItem>();
- Column idColumn = DataHelper.GetColumn(Report.Dictionary, dataBand.IdColumn);
- if (idColumn == null)
- return null;
- Column parentIdColumn = DataHelper.GetColumn(Report.Dictionary, dataBand.ParentIdColumn);
- if (parentIdColumn == null)
- return null;
- for (int i = 0; i < rowCount; i++)
- {
- object idColumnValue = dataBand.DataSource[idColumn];
- object parentIdColumnValue = dataBand.DataSource[parentIdColumn];
- string id = (idColumnValue == null || idColumnValue is DBNull) ? "" : idColumnValue.ToString();
- string parentId = (parentIdColumnValue == null || parentIdColumnValue is DBNull) ? "" :
- parentIdColumnValue.ToString();
- // avoid wrong behavior if wrong data specified
- if (id == parentId)
- parentId = "";
- HierarchyItem item = null;
- if (items.ContainsKey(id))
- item = items[id];
- else
- {
- item = new HierarchyItem();
- items.Add(id, item);
- }
- item.rowNo = dataBand.DataSource.CurrentRowNo;
- HierarchyItem parentItem = null;
- if (items.ContainsKey(parentId))
- parentItem = items[parentId];
- else
- {
- parentItem = new HierarchyItem();
- items.Add(parentId, parentItem);
- }
- parentItem.Add(item);
- dataBand.DataSource.Next();
- }
- // create the root item
- HierarchyItem rootItem = new HierarchyItem();
- foreach (HierarchyItem item in items.Values)
- {
- if (item.parent == null)
- {
- foreach (HierarchyItem childItem in item.items)
- {
- rootItem.Add(childItem);
- }
- }
- }
- return rootItem;
- }
- private void ShowHierarchy(DataBand dataBand, HierarchyItem rootItem, int level, string fullRowNo)
- {
- int saveLevel = hierarchyLevel;
- float saveIndent = hierarchyIndent;
- string saveRowNo = hierarchyRowNo;
- hierarchyLevel = level;
- hierarchyIndent = dataBand.Indent * (level - 1);
- try
- {
- if (rootItem.items.Count > 0)
- {
- ShowBand(dataBand.Header);
- int rowNo = 0;
- foreach (HierarchyItem item in rootItem.items)
- {
- rowNo++;
- dataBand.RowNo = rowNo;
- dataBand.AbsRowNo++;
- dataBand.DataSource.CurrentRowNo = item.rowNo;
- hierarchyRowNo = fullRowNo + rowNo.ToString();
- // show the main hierarchy band
- ShowBand(dataBand);
- // show sub-bands if any
- RunBands(dataBand.Bands);
- ShowHierarchy(dataBand, item, level + 1, hierarchyRowNo + ".");
- // up the outline
- OutlineUp(dataBand);
- }
- ShowBand(dataBand.Footer);
- }
- }
- finally
- {
- hierarchyLevel = saveLevel;
- hierarchyIndent = saveIndent;
- hierarchyRowNo = saveRowNo;
- }
- }
- private void ShowHierarchy(DataBand dataBand, int rowCount)
- {
- HierarchyItem rootItem = MakeHierarchy(dataBand, rowCount);
- if (rootItem == null)
- return;
- ShowHierarchy(dataBand, rootItem, 1, "");
- }
- private void ShowDataHeader(DataBand dataBand)
- {
- DataHeaderBand header = dataBand.Header;
- if (header != null)
- {
- ShowBand(header);
- if (header.RepeatOnEveryPage)
- AddReprint(header);
- }
- DataFooterBand footer = dataBand.Footer;
- if (footer != null)
- {
- if (footer.RepeatOnEveryPage)
- AddReprint(footer);
- }
- }
- private void ShowDataFooter(DataBand dataBand)
- {
- dataBand.DataSource.Prior();
- DataFooterBand footer = dataBand.Footer;
- RemoveReprint(footer);
- ShowBand(footer);
- RemoveReprint(dataBand.Header);
- dataBand.DataSource.Next();
- }
- #endregion Private Methods
- }
- }
|