using System;
using System.Collections.Generic;
using System.Drawing;
using System.ComponentModel;
using FastReport.Utils;
namespace FastReport.Table
{
///
/// Specifies the layout that will be used when printing a big table.
///
public enum TableLayout
{
///
/// The table is printed across a pages then down.
///
AcrossThenDown,
///
/// The table is printed down then across a pages.
///
DownThenAcross,
///
/// The table is wrapped.
///
Wrapped
}
///
/// The base class for table-type controls such as and
/// .
///
public partial class TableBase : BreakableComponent, IParent
{
#region Fields
private TableRowCollection rows;
private TableColumnCollection columns;
private TableStyleCollection styles;
private int fixedRows;
private int fixedColumns;
private bool printOnParent;
private float wrappedGap;
private bool repeatHeaders;
private bool repeatRowHeaders;
private bool repeatColumnHeaders;
private bool adjustSpannedCellsWidth;
private bool lockCorrectSpans;
private bool serializingToPreview;
private bool lockColumnRowChange;
private TableLayout layout;
private List spanList;
private TableResult resultTable;
private TableCellData printingCell;
//private static float FLeftRtl;
#endregion
#region Properties
///
/// Gets a collection of table rows.
///
[Browsable(false)]
public TableRowCollection Rows
{
get { return rows; }
}
///
/// Gets a collection of table columns.
///
[Browsable(false)]
public TableColumnCollection Columns
{
get { return columns; }
}
internal TableStyleCollection Styles
{
get { return styles; }
}
///
/// Gets or sets the number of fixed rows that will be repeated on each page.
///
[DefaultValue(0)]
[Category("Layout")]
public int FixedRows
{
get
{
int value = fixedRows;
if (value >= Rows.Count)
value = Rows.Count - 1;
if (value < 0)
value = 0;
return value;
}
set { fixedRows = value; }
}
///
/// Gets or sets the number of fixed columns that will be repeated on each page.
///
[DefaultValue(0)]
[Category("Layout")]
public int FixedColumns
{
get
{
int value = fixedColumns;
if (value >= Columns.Count)
value = Columns.Count - 1;
if (value < 0)
value = 0;
return value;
}
set { fixedColumns = value; }
}
///
/// Gets or sets the value that determines whether to print the dynamic table (or matrix) on its parent band directly.
///
///
/// By default the dynamic table (matrix) is printed on its own band and is splitted on pages if necessary.
///
[DefaultValue(false)]
[Category("Layout")]
public bool PrintOnParent
{
get { return printOnParent; }
set { printOnParent = value; }
}
///
/// Gets or sets a value that determines whether is necessary to repeat table header on each page.
///
///
/// To define a table header, set the and
/// properties.
///
[DefaultValue(true)]
[Category("Behavior")]
public bool RepeatHeaders
{
get { return repeatHeaders; }
set { repeatHeaders = value; }
}
///
/// Gets or sets a value that determines whether is necessary to repeat table Row header on each page.
///
///
/// To define a table Row header, set the
/// properties.
///
[Browsable(false)]
[DefaultValue(false)]
[Category("Behavior")]
public virtual bool RepeatRowHeaders
{
get { return repeatRowHeaders; }
set { repeatRowHeaders = value; }
}
///
/// Gets or sets a value that determines whether is necessary to repeat table Column header on each page.
///
///
/// To define a table Column header, set the
/// properties.
///
[Browsable(false)]
[DefaultValue(false)]
[Category("Behavior")]
public virtual bool RepeatColumnHeaders
{
get { return repeatColumnHeaders; }
set { repeatColumnHeaders = value; }
}
///
/// Gets or sets the table layout.
///
///
/// This property affects printing the big table that breaks across pages.
///
[DefaultValue(TableLayout.AcrossThenDown)]
[Category("Behavior")]
public TableLayout Layout
{
get { return layout; }
set { layout = value; }
}
///
/// Gets or sets gap between parts of the table in wrapped layout mode.
///
///
/// This property is used if you set the property to Wrapped.
///
[DefaultValue(0f)]
[Category("Behavior")]
[TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
public float WrappedGap
{
get { return wrappedGap; }
set { wrappedGap = value; }
}
///
/// Gets or sets a value that determines whether to adjust the spanned cell's width when breaking the table across pages.
///
///
/// If set to true, the spanned cell's width will be adjusted to accomodate all contained text.
///
[DefaultValue(false)]
[Category("Behavior")]
public bool AdjustSpannedCellsWidth
{
get { return adjustSpannedCellsWidth; }
set { adjustSpannedCellsWidth = value; }
}
///
/// Gets or sets the table cell.
///
/// Column index.
/// Row index.
/// The TableCell object that represents a cell.
[Browsable(false)]
public TableCell this[int col, int row]
{
get
{
if (col < 0 || col >= columns.Count || row < 0 || row >= rows.Count)
return null;
return rows[row][col];
}
set
{
if (col < 0 || col >= columns.Count || row < 0 || row >= rows.Count)
return;
rows[row][col] = value;
}
}
///
/// Gets or sets a number of columns in the table.
///
[Category("Appearance")]
public virtual int ColumnCount
{
get { return Columns.Count; }
set
{
int n = value - Columns.Count;
for (int i = 0; i < n; i++)
{
TableColumn column = new TableColumn();
Columns.Add(column);
}
while (value < Columns.Count)
Columns.RemoveAt(Columns.Count - 1);
}
}
///
/// Gets or sets a number of rows in the table.
///
[Category("Appearance")]
public virtual int RowCount
{
get { return Rows.Count; }
set
{
int n = value - Rows.Count;
for (int i = 0; i < n; i++)
{
TableRow row = new TableRow();
Rows.Add(row);
}
while (value < Rows.Count)
Rows.RemoveAt(Rows.Count - 1);
}
}
internal bool IsResultTable
{
get { return this is TableResult; }
}
///
/// Gets a table which contains the result of rendering dynamic table.
///
///
/// Use this property to access the result of rendering your table in dynamic mode.
/// It may be useful if you want to center or right-align the result table on a page.
/// In this case, you need to add the following code at the end of your ManualBuild event handler:
///
/// // right-align the table
/// Table1.ResultTable.Left = Engine.PageWidth - Table1.ResultTable.CalcWidth() - 1;
///
///
[Browsable(false)]
public TableResult ResultTable
{
get { return resultTable; }
}
internal TableCellData PrintingCell
{
get { return printingCell; }
set { printingCell = value; }
}
internal bool LockCorrectSpans
{
get { return lockCorrectSpans; }
set { lockCorrectSpans = value; }
}
#endregion
#region Private Methods
private delegate void DrawCellProc(FRPaintEventArgs e, TableCell cell);
private void DrawCells(FRPaintEventArgs e, DrawCellProc proc)
{
float top = 0;
for (int y = 0; y < Rows.Count; y++)
{
float left = 0;
float height = Rows[y].Height;
for (int x = 0; x < Columns.Count; x++)
{
TableCell cell = this[x, y];
float width = Columns[x].Width;
cell.Left = left;
cell.Top = top;
if (!IsInsideSpan(cell) && (!IsPrinting || cell.Printable))
{
cell.SetPrinting(IsPrinting);
#if !MONO || (WPF || AVALONIA)
if (cell.IsVisible(e))
#endif
proc(e, cell);
}
left += width;
}
top += height;
}
}
private void DrawCellsRtl(FRPaintEventArgs e, DrawCellProc proc)
{
float top = 0;
for (int y = 0; y < Rows.Count; y++)
{
float left = 0;
float height = Rows[y].Height;
//bool thereIsColSpan = false;
//for (int i = Columns.Count - 1; i >= 0; i--)
//{
// TableCell cell = this[i, y];
// if (cell.ColSpan > 1)
// {
// thereIsColSpan = true;
// }
//}
for (int x = Columns.Count - 1; x >= 0; x--)
{
TableCell cell = this[x, y];
bool thereIsColSpan = false;
if (cell.ColSpan > 1)
{
thereIsColSpan = true;
}
float width = Columns[x].Width;
//if (thereIsColSpan)
//{
// width *= cell.ColSpan - 1;
// left -= width;
//}
if (!IsInsideSpan(cell) && (!IsPrinting || cell.Printable))
{
cell.Left = left;
cell.Top = top;
cell.SetPrinting(IsPrinting);
proc(e, cell);
if (thereIsColSpan)
width *= cell.ColSpan;
left += width;
}
//if (!thereIsColSpan)
// left += width;
//else
// left -= width;
}
top += height;
}
}
private void DrawFill(FRPaintEventArgs e, TableCell cell)
{
cell.DrawBackground(e);
}
private void DrawText(FRPaintEventArgs e, TableCell cell)
{
cell.DrawText(e);
}
private void DrawBorders(FRPaintEventArgs e, TableCell cell)
{
cell.Border.Draw(e, cell.AbsBounds);
}
private void DrawTable(FRPaintEventArgs e)
{
DrawCells(e, DrawFill);
DrawCells(e, DrawText);
DrawDesign_Borders(e);
DrawCells(e, DrawBorders);
DrawDesign_SelectedCells(e);
}
private void DrawTableRtl(FRPaintEventArgs e)
{
DrawCellsRtl(e, DrawFill);
DrawCellsRtl(e, DrawText);
DrawDesign_BordersRtl(e);
DrawCellsRtl(e, DrawBorders);
DrawDesign_SelectedCellsRtl(e);
}
#endregion
#region Public Methods
///
public override void Assign(Base source)
{
base.Assign(source);
TableBase src = source as TableBase;
FixedRows = src.FixedRows;
FixedColumns = src.FixedColumns;
PrintOnParent = src.PrintOnParent;
RepeatHeaders = src.RepeatHeaders;
RepeatRowHeaders = src.RepeatRowHeaders;
RepeatColumnHeaders = src.RepeatColumnHeaders;
Layout = src.Layout;
WrappedGap = src.WrappedGap;
AdjustSpannedCellsWidth = src.AdjustSpannedCellsWidth;
}
///
public override void Draw(FRPaintEventArgs e)
{
if (ColumnCount == 0 || RowCount == 0)
return;
lockColumnRowChange = true;
Width = Columns[Columns.Count - 1].Right;
Height = Rows[Rows.Count - 1].Bottom;
lockColumnRowChange = false;
base.Draw(e);
// draw table Right to Left if needed
if (Config.RightToLeft)
{
DrawTableRtl(e);
}
else
{
DrawTable(e);
}
Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height));
DrawDesign(e);
}
///
public override bool IsVisible(FRPaintEventArgs e)
{
if (RowCount == 0 || ColumnCount == 0)
return false;
Width = Columns[Columns.Count - 1].Right;
Height = Rows[Rows.Count - 1].Bottom;
RectangleF objRect = new RectangleF(AbsLeft * e.ScaleX, AbsTop * e.ScaleY,
Width * e.ScaleX + 1, Height * e.ScaleY + 1);
return e.Graphics.IsVisible(objRect);
}
internal void SetResultTable(TableResult table)
{
resultTable = table;
}
///
/// Gets data of the table cell with specified column and row numbers.
///
/// The column number.
/// The row number.
/// TableCellData instance containing data of the table cell.
public TableCellData GetCellData(int col, int row)
{
if (col < 0 || col >= columns.Count || row < 0 || row >= rows.Count)
return null;
return rows[row].CellData(col);
}
internal List GetSpanList()
{
if (spanList == null)
{
spanList = new List();
for (int y = 0; y < Rows.Count; y++)
{
for (int x = 0; x < Columns.Count; x++)
{
TableCellData cell = GetCellData(x, y);
if (cell.ColSpan > 1 || cell.RowSpan > 1)
spanList.Add(new Rectangle(x, y, cell.ColSpan, cell.RowSpan));
}
}
}
return spanList;
}
internal void ResetSpanList()
{
spanList = null;
}
internal void CorrectSpansOnRowChange(int rowIndex, int correct)
{
if (lockCorrectSpans || (correct == 1 && rowIndex >= Rows.Count))
return;
for (int y = 0; y < rowIndex; y++)
{
for (int x = 0; x < Columns.Count; x++)
{
TableCellData cell = GetCellData(x, y);
if (rowIndex < y + cell.RowSpan)
cell.RowSpan += correct;
}
}
ResetSpanList();
}
internal void CorrectSpansOnColumnChange(int columnIndex, int correct)
{
if (lockCorrectSpans || (correct == 1 && columnIndex >= Columns.Count))
return;
for (int y = 0; y < Rows.Count; y++)
{
for (int x = 0; x < columnIndex; x++)
{
TableCellData cell = GetCellData(x, y);
if (columnIndex < x + cell.ColSpan)
cell.ColSpan += correct;
}
// correct cells
Rows[y].CorrectCellsOnColumnChange(columnIndex, correct);
}
ResetSpanList();
}
public bool IsInsideSpan(TableCell cell)
{
Point address = cell.Address;
List spans = GetSpanList();
foreach (Rectangle span in spans)
{
if (span.Contains(address) && span.Location != address)
return true;
}
return false;
}
///
/// Creates unique names for all table elements such as rows, columns, cells.
///
public void CreateUniqueNames()
{
if (Report == null)
return;
FastNameCreator nameCreator = new FastNameCreator(Report.AllNamedObjects);
foreach (TableRow row in Rows)
{
if (String.IsNullOrEmpty(row.Name))
nameCreator.CreateUniqueName(row);
}
foreach (TableColumn column in Columns)
{
if (String.IsNullOrEmpty(column.Name))
nameCreator.CreateUniqueName(column);
}
for (int y = 0; y < Rows.Count; y++)
{
for (int x = 0; x < Columns.Count; x++)
{
TableCell cell = this[x, y];
if (String.IsNullOrEmpty(cell.Name))
{
nameCreator.CreateUniqueName(cell);
cell.Font = DrawUtils.DefaultReportFont;
}
if (cell.Objects != null)
{
foreach (ReportComponentBase obj in cell.Objects)
{
if (String.IsNullOrEmpty(obj.Name))
nameCreator.CreateUniqueName(obj);
}
}
}
}
}
///
public override void Serialize(FRWriter writer)
{
TableBase c = writer.DiffObject as TableBase;
serializingToPreview = writer.SerializeTo == SerializeTo.Preview;
base.Serialize(writer);
if (FixedRows != c.FixedRows)
writer.WriteInt("FixedRows", FixedRows);
if (FixedColumns != c.FixedColumns)
writer.WriteInt("FixedColumns", FixedColumns);
if (PrintOnParent != c.PrintOnParent)
writer.WriteBool("PrintOnParent", PrintOnParent);
if (RepeatHeaders != c.RepeatHeaders)
writer.WriteBool("RepeatHeaders", RepeatHeaders);
if (RepeatRowHeaders != c.RepeatRowHeaders)
writer.WriteBool("RepeatRowHeaders", RepeatRowHeaders);
if (RepeatColumnHeaders != c.RepeatColumnHeaders)
writer.WriteBool("RepeatColumnHeaders", RepeatColumnHeaders);
if (Layout != c.Layout)
writer.WriteValue("Layout", Layout);
if (WrappedGap != c.WrappedGap)
writer.WriteFloat("WrappedGap", WrappedGap);
if (AdjustSpannedCellsWidth != c.AdjustSpannedCellsWidth)
writer.WriteBool("AdjustSpannedCellsWidth", AdjustSpannedCellsWidth);
}
internal void EmulateOuterBorder()
{
for (int y = 0; y < RowCount; y++)
{
for (int x = 0; x < ColumnCount; x++)
{
TableCell cell = this[x, y];
if (x == 0 && (Border.Lines & BorderLines.Left) != 0)
{
cell.Border.LeftLine.Assign(Border.LeftLine);
cell.Border.Lines |= BorderLines.Left;
}
if (x + cell.ColSpan == ColumnCount && (Border.Lines & BorderLines.Right) != 0)
{
cell.Border.RightLine.Assign(Border.RightLine);
cell.Border.Lines |= BorderLines.Right;
}
if (y == 0 && (Border.Lines & BorderLines.Top) != 0)
{
cell.Border.TopLine.Assign(Border.TopLine);
cell.Border.Lines |= BorderLines.Top;
}
if (y + cell.RowSpan == RowCount && (Border.Lines & BorderLines.Bottom) != 0)
{
cell.Border.BottomLine.Assign(Border.BottomLine);
cell.Border.Lines |= BorderLines.Bottom;
}
}
}
}
#endregion
#region IParent Members
///
public bool CanContain(Base child)
{
return child is TableRow || child is TableColumn || child is TableCell;
}
///
public virtual void GetChildObjects(ObjectCollection list)
{
foreach (TableColumn column in Columns)
{
// Apply visible expression if needed.
if (serializingToPreview && !String.IsNullOrEmpty(column.VisibleExpression))
column.Visible = CalcVisibleExpression(column.VisibleExpression);
if (!serializingToPreview || column.Visible)
list.Add(column);
}
foreach (TableRow row in Rows)
{
// Apply visible expression if needed.
if (serializingToPreview && !String.IsNullOrEmpty(row.VisibleExpression))
row.Visible = CalcVisibleExpression(row.VisibleExpression);
if (!serializingToPreview || row.Visible)
list.Add(row);
}
}
///
public void AddChild(Base child)
{
if (child is TableRow)
Rows.Add(child as TableRow);
else if (child is TableColumn)
Columns.Add(child as TableColumn);
}
///
public void RemoveChild(Base child)
{
if (child is TableRow)
Rows.Remove(child as TableRow);
else if (child is TableColumn)
Columns.Remove(child as TableColumn);
}
///
public int GetChildOrder(Base child)
{
if (child is TableColumn)
return Columns.IndexOf(child as TableColumn);
else if (child is TableRow)
return Rows.IndexOf(child as TableRow);
return 0;
}
///
public void SetChildOrder(Base child, int order)
{
lockCorrectSpans = true;
int oldOrder = child.ZOrder;
if (oldOrder != -1 && order != -1 && oldOrder != order)
{
if (child is TableColumn)
{
if (order > Columns.Count)
order = Columns.Count;
if (oldOrder <= order)
order--;
Columns.Remove(child as TableColumn);
Columns.Insert(order, child as TableColumn);
}
else if (child is TableRow)
{
if (order > Rows.Count)
order = Rows.Count;
if (oldOrder <= order)
order--;
Rows.Remove(child as TableRow);
Rows.Insert(order, child as TableRow);
}
}
lockCorrectSpans = false;
}
///
public void UpdateLayout(float dx, float dy)
{
}
#endregion
#region Report Engine
///
public override void SaveState()
{
base.SaveState();
foreach (TableRow row in Rows)
{
row.SaveState();
}
foreach (TableColumn column in Columns)
{
column.SaveState();
}
for (int y = 0; y < Rows.Count; y++)
{
for (int x = 0; x < Columns.Count; x++)
{
this[x, y].SaveState();
}
}
CanGrow = true;
CanShrink = true;
}
///
public override void RestoreState()
{
base.RestoreState();
foreach (TableRow row in Rows)
{
row.RestoreState();
}
foreach (TableColumn column in Columns)
{
column.RestoreState();
}
for (int y = 0; y < Rows.Count; y++)
{
for (int x = 0; x < Columns.Count; x++)
{
this[x, y].RestoreState();
}
}
}
///
/// Calculates and returns the table width, in pixels.
///
public float CalcWidth()
{
// first pass, calc non-spanned cells
for (int x = 0; x < Columns.Count; x++)
{
TableColumn column = Columns[x];
if (!column.AutoSize)
continue;
float columnWidth = IsDesigning ? 16 : -1;
// calc the max column width
for (int y = 0; y < Rows.Count; y++)
{
TableCellData cell = GetCellData(x, y);
if (cell.ColSpan == 1)
{
float cellWidth = cell.CalcWidth();
if (cellWidth > columnWidth)
columnWidth = cellWidth;
}
}
// update column width
if (columnWidth != -1)
column.Width = columnWidth;
}
// second pass, calc spanned cells
for (int x = 0; x < Columns.Count; x++)
{
Columns[x].MinimumBreakWidth = 0;
for (int y = 0; y < Rows.Count; y++)
{
TableCellData cell = GetCellData(x, y);
if (cell.ColSpan > 1)
{
float cellWidth = cell.CalcWidth();
if (AdjustSpannedCellsWidth && cellWidth > Columns[x].MinimumBreakWidth)
Columns[x].MinimumBreakWidth = cellWidth;
// check that spanned columns have enough width
float columnsWidth = 0;
for (int i = 0; i < cell.ColSpan; i++)
{
columnsWidth += Columns[Math.Min(Columns.Count-1,x + i)].Width;
}
// if cell is bigger than sum of column width, increase the last column width
TableColumn lastColumn = Columns[Math.Min(Columns.Count-1,x + cell.ColSpan - 1)];
if (columnsWidth < cellWidth && lastColumn.AutoSize)
lastColumn.Width += cellWidth - columnsWidth;
}
}
}
// finally, calculate the table width
float width = 0;
for (int i = 0; i < Columns.Count; i++)
{
width += Columns[i].Width;
}
lockColumnRowChange = true;
Width = width;
lockColumnRowChange = false;
return width;
}
///
public override float CalcHeight()
{
if (ColumnCount * RowCount > 1000)
Config.ReportSettings.OnProgress(Report, Res.Get("ComponentsMisc,Table,CalcBounds"), 0, 0);
// calc width
CalcWidth();
// first pass, calc non-spanned cells
for (int y = 0; y < Rows.Count; y++)
{
TableRow row = Rows[y];
if (!row.AutoSize)
continue;
float rowHeight = IsDesigning ? 16 : -1;
// calc the max row height
for (int x = 0; x < Columns.Count; x++)
{
TableCellData cell = GetCellData(x, y);
if (cell.RowSpan == 1)
{
float cellHeight = cell.CalcHeight(cell.Width);
if (cellHeight > rowHeight)
rowHeight = cellHeight;
}
}
// update row height
if (rowHeight != -1)
row.Height = rowHeight;
}
// second pass, calc spanned cells
for (int y = 0; y < Rows.Count; y++)
{
for (int x = 0; x < Columns.Count; x++)
{
TableCellData cell = GetCellData(x, y);
if (cell.RowSpan > 1)
{
float cellHeight = cell.CalcHeight(cell.Width);
// check that spanned rows have enough height
float rowsHeight = 0;
for (int i = 0; i < cell.RowSpan; i++)
{
if (y + i < Rows.Count)
rowsHeight += Rows[y + i].Height;
else
{
// Error, we don't have row, rowSpan has incorrect
cell.RowSpan--;
}
}
// if cell is bigger than sum of row heights, increase the last row height
if (y + cell.RowSpan - 1 < Rows.Count)
{
TableRow lastRow = Rows[y + cell.RowSpan - 1];
if (rowsHeight < cellHeight && lastRow.AutoSize)
lastRow.Height += cellHeight - rowsHeight;
}
}
}
}
// finally, calculate the table height
float height = 0;
for (int i = 0; i < Rows.Count; i++)
{
height += Rows[i].Visible ? Rows[i].Height : 0;
}
return height;
}
private bool CanBreakRow(int rowIndex, float rowHeight)
{
if (!Rows[rowIndex].CanBreak)
return false;
// check each cell in the row
for (int i = 0; i < ColumnCount; i++)
{
TableCell breakable = this[i, rowIndex];
// use clone object because Break method will modify the Text property
using (TableCell clone = new TableCell())
{
clone.AssignAll(breakable);
clone.Height = rowHeight;
clone.SetReport(Report);
if (!clone.Break(null))
return false;
}
}
return true;
}
private void BreakRow(TableBase breakTo, int rowIndex, float rowHeight, float newRowHeight)
{
// set rows height
TableRow rowTo = breakTo.Rows[rowIndex];
Rows[rowIndex].Height = rowHeight;
rowTo.Height = newRowHeight;
// break each cell in the row
for (int i = 0; i < ColumnCount; i++)
{
TableCell cell = this[i, rowIndex];
TableCell cellTo = breakTo[i, rowIndex];
cell.Height = rowHeight;
cell.Break(cellTo);
// fix height if row is not autosized
if (!rowTo.AutoSize)
{
float h = cellTo.CalcHeight();
if (h > rowTo.Height)
rowTo.Height = h;
}
}
}
///
public override bool Break(BreakableComponent breakTo)
{
if (Rows.Count == 0)
return true;
if (Height < Rows[0].Height && !Rows[0].CanBreak)
return false;
TableBase tableTo = breakTo as TableBase;
if (tableTo == null)
return true;
// find the break row index
int breakRowIndex = 0;
int breakRowIndexAdd = 0;
bool rowBroken = false;
float rowsHeight = 0;
while (breakRowIndex < Rows.Count)
{
rowsHeight += Rows[breakRowIndex].Height;
if (rowsHeight > Height)
{
float breakRowHeight = Rows[breakRowIndex].Height - (rowsHeight - Height);
if (CanBreakRow(breakRowIndex, breakRowHeight))
{
BreakRow(tableTo, breakRowIndex, breakRowHeight, rowsHeight - Height);
breakRowIndexAdd = 1;
rowBroken = true;
}
break;
}
breakRowIndex++;
}
// get the span list
List spans = GetSpanList();
// break the spans
foreach (Rectangle span in spans)
{
if (span.Top < breakRowIndex + breakRowIndexAdd && span.Bottom > breakRowIndex)
{
TableCell cell = this[span.Left, span.Top];
TableCell cellTo = tableTo[span.Left, span.Top];
// update cell spans
cell.RowSpan = breakRowIndex + breakRowIndexAdd - span.Top;
cellTo.RowSpan = span.Bottom - breakRowIndex;
// break the cell
if (!rowBroken && !cell.Break(cellTo))
cell.Text = "";
// set the top span cell to the correct place
tableTo[span.Left, span.Top] = new TableCell();
tableTo[span.Left, breakRowIndex] = cellTo;
}
}
// remove unused rows from source (this table)
while (breakRowIndex + breakRowIndexAdd < Rows.Count)
{
this.Rows.RemoveAt(Rows.Count - 1);
}
// remove unused rows from copy (tableTo)
for (int i = 0; i < breakRowIndex; i++)
{
tableTo.Rows.RemoveAt(0);
}
return true;
}
private List GetAggregateCells(TableCell aggregateCell)
{
List list = new List();
// columnIndex, rowIndex is a place where we will print a result.
// To collect aggregate values that will be used to calculate a result, we need to go
// to the left and top from this point and collect every cell which OriginalCell is equal to
// the aggregateCell value. We have to stop when we meet the same row or column.
int columnIndex = PrintingCell.Address.X;
int rowIndex = PrintingCell.Address.Y;
TableColumn startColumn = ResultTable.Columns[columnIndex];
TableRow startRow = ResultTable.Rows[rowIndex];
TableColumn aggregateColumn = Columns[aggregateCell.Address.X];
TableRow aggregateRow = Rows[aggregateCell.Address.Y];
// check if result is in the same row/column as aggregate cell
bool sameRow = startRow.OriginalComponent == aggregateRow.OriginalComponent;
bool sameColumn = startColumn.OriginalComponent == aggregateColumn.OriginalComponent;
for (int y = rowIndex; y >= 0; y--)
{
if (y != rowIndex && ResultTable.Rows[y].OriginalComponent == startRow.OriginalComponent)
break;
for (int x = columnIndex; x >= 0; x--)
{
if (x != columnIndex && ResultTable.Columns[x].OriginalComponent == startColumn.OriginalComponent)
break;
TableCellData cell = ResultTable.GetCellData(x, y);
if (cell.OriginalCell == aggregateCell)
list.Add(cell);
if (sameColumn)
break;
}
if (sameRow)
break;
}
return list;
}
///
/// Calculates a sum of values in a specified cell.
///
/// The cell.
/// The object that contains calculated value.
///
/// This method can be called from the ManualBuild event handler only.
///
public object Sum(TableCell aggregateCell)
{
List list = GetAggregateCells(aggregateCell);
Variant result = 0;
bool firstTime = true;
foreach (TableCellData cell in list)
{
if (cell.Value != null)
{
Variant varValue = new Variant(cell.Value);
if (firstTime)
result = varValue;
else
result += varValue;
firstTime = false;
}
}
return result.Value;
}
///
/// Calculates a minimum of values in a specified cell.
///
/// The cell.
/// The object that contains calculated value.
///
/// This method can be called from the ManualBuild event handler only.
///
public object Min(TableCell aggregateCell)
{
List list = GetAggregateCells(aggregateCell);
Variant result = float.PositiveInfinity;
bool firstTime = true;
foreach (TableCellData cell in list)
{
if (cell.Value != null)
{
Variant varValue = new Variant(cell.Value);
if (firstTime || varValue < result)
result = varValue;
firstTime = false;
}
}
return result.Value;
}
///
/// Calculates a maximum of values in a specified cell.
///
/// The cell.
/// The object that contains calculated value.
///
/// This method can be called from the ManualBuild event handler only.
///
public object Max(TableCell aggregateCell)
{
List list = GetAggregateCells(aggregateCell);
Variant result = float.NegativeInfinity;
bool firstTime = true;
foreach (TableCellData cell in list)
{
if (cell.Value != null)
{
Variant varValue = new Variant(cell.Value);
if (firstTime || varValue > result)
result = varValue;
firstTime = false;
}
}
return result.Value;
}
///
/// Calculates an average of values in a specified cell.
///
/// The cell.
/// The object that contains calculated value.
///
/// This method can be called from the ManualBuild event handler only.
///
public object Avg(TableCell aggregateCell)
{
List list = GetAggregateCells(aggregateCell);
Variant result = 0;
int count = 0;
bool firstTime = true;
foreach (TableCellData cell in list)
{
if (cell.Value != null)
{
Variant varValue = new Variant(cell.Value);
if (firstTime)
result = varValue;
else
result += varValue;
count++;
firstTime = false;
}
}
return result / (count == 0 ? 1 : count);
}
///
/// Calculates number of repeats of a specified cell.
///
/// The cell.
/// The object that contains calculated value.
///
/// This method can be called from the ManualBuild event handler only.
///
public object Count(TableCell aggregateCell)
{
List list = GetAggregateCells(aggregateCell);
int count = 0;
foreach (TableCellData cell in list)
{
if (cell.Value != null)
count++;
}
return count;
}
#endregion
///
/// Initializes a new instance of the class.
///
public TableBase()
{
rows = new TableRowCollection(this);
columns = new TableColumnCollection(this);
styles = new TableStyleCollection();
repeatHeaders = true;
repeatRowHeaders = false;
repeatColumnHeaders = false;
CanGrow = true;
CanShrink = true;
}
}
}