using System; using System.Drawing; using System.Windows.Forms; namespace FastReport.Table { /// /// Represents data of the table cell. /// public class TableCellData : IDisposable { #region Fields private string text; private object value; private string hyperlinkValue; private int colSpan; private int rowSpan; private ReportComponentCollection objects; private TableCell style; private TableCell cell; private TableBase table; private Point address; private bool updatingLayout; #endregion // Fields #region Properties /// /// Gets or sets parent table of the cell. /// public TableBase Table { get { return table; } set { table = value; } } /// /// Gets or sets objects collection of the cell. /// public ReportComponentCollection Objects { get { return objects; } set { objects = value; } } /// /// Gets or sets text of the table cell. /// public string Text { get { return text; } set { text = value; } } /// /// Gets or sets value of the table cell. /// public object Value { get { return value; } set { this.value = value; } } /// /// Gets or sets hyperlink value of the table cell. /// public string HyperlinkValue { get { return hyperlinkValue; } set { hyperlinkValue = value; } } /// /// Gets or sets column span of the table cell. /// public int ColSpan { get { return colSpan; } set { if (colSpan != value) { float oldWidth = Width; colSpan = value; if (Table != null) { Table.ResetSpanList(); UpdateLayout(oldWidth, Height, Width - oldWidth, 0); } } } } /// /// Gets or sets row span of the table cell. /// public int RowSpan { get { return rowSpan; } set { if (rowSpan != value) { float oldHeight = Height; rowSpan = value; if (Table != null) { Table.ResetSpanList(); UpdateLayout(Width, oldHeight, 0, Height - oldHeight); } } } } /// /// Gets or sets the address of the table cell. /// public Point Address { get { return address; } set { address = value; } } /// /// Gets the table cell. /// public TableCell Cell { get { if (Table.IsResultTable) { TableCell cell0 = style; if (cell0 == null) cell0 = Table.Styles.DefaultStyle; if (this.cell != null) { cell0.Alias = this.cell.Alias; cell0.OriginalComponent = this.cell.OriginalComponent; } // handling dock/anchor of cell objects correctly: detach old celldata, update size, attach new one. cell0.CellData = null; cell0.Width = Width; cell0.Height = Height; cell0.CellData = this; cell0.Hyperlink.Value = HyperlinkValue; return cell0; } if (cell == null) { cell = new TableCell(); cell.CellData = this; } return cell; } } /// /// Gets style of table cell. /// public TableCell Style { get { return style; } } /// /// Gets original the table cell. /// public TableCell OriginalCell { get { return cell; } } /// /// Gets width of the table cell. /// public float Width { get { if (Table == null) return 0; float result = 0; for (int i = 0; i < ColSpan; i++) { if (Address.X + i < Table.Columns.Count) result += Table.Columns[Address.X + i].Width; } return result; } } /// /// Gets height of the table cell. /// public float Height { get { if (Table == null) return 0; float result = 0; for (int i = 0; i < RowSpan; i++) { if (Address.Y + i < Table.Rows.Count) result += Table.Rows[Address.Y + i].Height; } return result; } } #endregion // Properties #region Constructors /// /// Initializes a new instance of the class. /// public TableCellData() { colSpan = 1; rowSpan = 1; text = ""; hyperlinkValue = ""; } #endregion // Constructors #region Public Methods /// /// Attaches the specified table cell. /// /// The table cell instance. /// This method is called when we load the table. public void AttachCell(TableCell cell) { if (this.cell != null) { this.cell.CellData = null; this.cell.Dispose(); } Text = cell.Text; ColSpan = cell.ColSpan; RowSpan = cell.RowSpan; objects = cell.Objects; style = null; this.cell = cell; cell.CellData = this; } /// /// Assigns another instance. /// /// The table cell data that used as a source. /// This method is called when we copy cells or clone columns/rows in a designer. public void Assign(TableCellData source) { AttachCell(source.Cell); } /// /// Assigns another instance at run time. /// /// The table cell data that used as a source. /// This flag shows should children be copied or not. /// This method is called when we print a table. We should create a copy of the cell and set the style. public void RunTimeAssign(TableCell cell, bool copyChildren) { Text = cell.Text; Value = cell.Value; HyperlinkValue = cell.Hyperlink.Value; // don't copy ColSpan, RowSpan - they will be handled in the TableHelper. //ColSpan = cell.ColSpan; //RowSpan = cell.RowSpan; // clone objects objects = null; if (cell.Objects != null && copyChildren) { objects = new ReportComponentCollection(); foreach (ReportComponentBase obj in cell.Objects) { if (obj.Visible) { ReportComponentBase cloneObj = Activator.CreateInstance(obj.GetType()) as ReportComponentBase; cloneObj.AssignAll(obj); cloneObj.Name = obj.Name; objects.Add(cloneObj); } } } // add the cell to the style list. If the list contains such style, // return the existing style; in other case, create new style based // on the given cell. SetStyle(cell); // cell is used to reference the original cell. It is necessary to use Alias, OriginalComponent this.cell = cell; // reset object's size as if we set ColSpan and RowSpan to 1. // It is nesessary when printing spanned cells because the span of such cells will be corrected // when print new rows/columns and thus will move cell objects. if (objects != null) UpdateLayout(cell.Width, cell.Height, Width - cell.Width, Height - cell.Height); } /// /// Sets style of the table cell. /// /// The new style of the table cell. public void SetStyle(TableCell style) { this.style = Table.Styles.Add(style); } /// /// Disposes the instance. /// public void Dispose() { if (style == null && cell != null) cell.Dispose(); cell = null; style = null; } /// /// Calculates width of the table cell. /// /// The value of the table cell width. public float CalcWidth() { TableCell cell = Cell; cell.SetReport(Table.Report); return cell.CalcWidth(); } /// /// Calculates height of the table cell. /// /// The width of the table cell. /// The value of the table cell height. public float CalcHeight(float width) { TableCell cell = Cell; cell.SetReport(Table.Report); cell.Width = width; float cellHeight = cell.CalcHeight(); if (objects != null) { // pasted from BandBase.cs // sort objects by Top ReportComponentCollection sortedObjects = objects.SortByTop(); // calc height of each object float[] heights = new float[sortedObjects.Count]; for (int i = 0; i < sortedObjects.Count; i++) { ReportComponentBase obj = sortedObjects[i]; float height = obj.Height; if (obj.CanGrow || obj.CanShrink) { float height1 = obj.CalcHeight(); if ((obj.CanGrow && height1 > height) || (obj.CanShrink && height1 < height)) height = height1; } heights[i] = height; } // calc shift amounts float[] shifts = new float[sortedObjects.Count]; for (int i = 0; i < sortedObjects.Count; i++) { ReportComponentBase parent = sortedObjects[i]; float shift = heights[i] - parent.Height; if (shift == 0) continue; for (int j = i + 1; j < sortedObjects.Count; j++) { ReportComponentBase child = sortedObjects[j]; if (child.ShiftMode == ShiftMode.Never) continue; if (child.Top >= parent.Bottom - 1e-4) { if (child.ShiftMode == ShiftMode.WhenOverlapped && (child.Left > parent.Right - 1e-4 || parent.Left > child.Right - 1e-4)) continue; float parentShift = shifts[i]; float childShift = shifts[j]; if (shift > 0) childShift = Math.Max(shift + parentShift, childShift); else childShift = Math.Min(shift + parentShift, childShift); shifts[j] = childShift; } } } // update location and size of each component, calc max height float maxHeight = 0; for (int i = 0; i < sortedObjects.Count; i++) { ReportComponentBase obj = sortedObjects[i]; obj.Height = heights[i]; obj.Top += shifts[i]; if (obj.Bottom > maxHeight) maxHeight = obj.Bottom; } if (cellHeight < maxHeight) cellHeight = maxHeight; // perform grow to bottom foreach (ReportComponentBase obj in objects) { if (obj.GrowToBottom) obj.Height = cellHeight - obj.Top; } // ----------------------- } return cellHeight; } internal void UpdateLayout(float dx, float dy) { UpdateLayout(Width, Height, dx, dy); } internal void UpdateLayout(float width, float height, float dx, float dy) { if (updatingLayout || Objects == null) return; updatingLayout = true; try { RectangleF remainingBounds = new RectangleF(0, 0, width, height); remainingBounds.Width += dx; remainingBounds.Height += dy; foreach (ReportComponentBase c in Objects) { if ((c.Anchor & AnchorStyles.Right) != 0) { if ((c.Anchor & AnchorStyles.Left) != 0) c.Width += dx; else c.Left += dx; } else if ((c.Anchor & AnchorStyles.Left) == 0) { c.Left += dx / 2; } if ((c.Anchor & AnchorStyles.Bottom) != 0) { if ((c.Anchor & AnchorStyles.Top) != 0) c.Height += dy; else c.Top += dy; } else if ((c.Anchor & AnchorStyles.Top) == 0) { c.Top += dy / 2; } switch (c.Dock) { case DockStyle.Left: c.Bounds = new RectangleF(remainingBounds.Left, remainingBounds.Top, c.Width, remainingBounds.Height); remainingBounds.X += c.Width; remainingBounds.Width -= c.Width; break; case DockStyle.Top: c.Bounds = new RectangleF(remainingBounds.Left, remainingBounds.Top, remainingBounds.Width, c.Height); remainingBounds.Y += c.Height; remainingBounds.Height -= c.Height; break; case DockStyle.Right: c.Bounds = new RectangleF(remainingBounds.Right - c.Width, remainingBounds.Top, c.Width, remainingBounds.Height); remainingBounds.Width -= c.Width; break; case DockStyle.Bottom: c.Bounds = new RectangleF(remainingBounds.Left, remainingBounds.Bottom - c.Height, remainingBounds.Width, c.Height); remainingBounds.Height -= c.Height; break; case DockStyle.Fill: c.Bounds = remainingBounds; remainingBounds.Width = 0; remainingBounds.Height = 0; break; } } } finally { updatingLayout = false; } } #endregion // Public Methods } }