using FastReport.Utils; using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace FastReport { /// /// Container object that may contain child objects. /// public partial class ContainerObject : ReportComponentBase, IParent { #region Fields private ReportComponentCollection objects; private bool updatingLayout; private string beforeLayoutEvent; private string afterLayoutEvent; private int savedOriginalObjectsCount; #endregion #region Properties /// /// Gets the collection of child objects. /// [Browsable(false)] public ReportComponentCollection Objects { get { return objects; } } /// /// This event occurs before the container layouts its child objects. /// public event EventHandler BeforeLayout; /// /// This event occurs after the child objects layout was finished. /// public event EventHandler AfterLayout; /// /// Gets or sets a script event name that will be fired before the container layouts its child objects. /// [Category("Build")] public string BeforeLayoutEvent { get { return beforeLayoutEvent; } set { beforeLayoutEvent = value; } } /// /// Gets or sets a script event name that will be fired after the child objects layout was finished. /// [Category("Build")] public string AfterLayoutEvent { get { return afterLayoutEvent; } set { afterLayoutEvent = value; } } #endregion #region IParent /// public virtual void GetChildObjects(ObjectCollection list) { foreach (ReportComponentBase c in objects) { list.Add(c); } } /// public virtual bool CanContain(Base child) { return (child is ReportComponentBase); } /// public virtual void AddChild(Base child) { if (child is ReportComponentBase) objects.Add(child as ReportComponentBase); } /// public virtual void RemoveChild(Base child) { if (child is ReportComponentBase) objects.Remove(child as ReportComponentBase); } /// public virtual int GetChildOrder(Base child) { return objects.IndexOf(child as ReportComponentBase); } /// public virtual void SetChildOrder(Base child, int order) { int oldOrder = child.ZOrder; if (oldOrder != -1 && order != -1 && oldOrder != order) { if (order > objects.Count) order = objects.Count; if (oldOrder <= order) order--; objects.Remove(child as ReportComponentBase); objects.Insert(order, child as ReportComponentBase); } } /// public virtual void UpdateLayout(float dx, float dy) { if (updatingLayout) 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 #region Report engine /// public override void SaveState() { base.SaveState(); savedOriginalObjectsCount = Objects.Count; SetRunning(true); SetDesigning(false); foreach (ReportComponentBase obj in Objects) { obj.SaveState(); obj.SetRunning(true); obj.SetDesigning(false); obj.OnBeforePrint(EventArgs.Empty); } } /// public override void RestoreState() { base.RestoreState(); while (Objects.Count > savedOriginalObjectsCount) { Objects[Objects.Count - 1].Dispose(); } SetRunning(false); foreach (ReportComponentBase obj in Objects) { obj.OnAfterPrint(EventArgs.Empty); obj.RestoreState(); obj.SetRunning(false); } } /// public override void GetData() { base.GetData(); var objArray = Objects.ToArray(); foreach (ReportComponentBase obj in objArray) { obj.GetData(); obj.OnAfterData(); // break the component if it is of BreakableComponent an has non-empty BreakTo property if (obj is BreakableComponent && (obj as BreakableComponent).BreakTo != null && (obj as BreakableComponent).BreakTo.GetType() == obj.GetType()) (obj as BreakableComponent).Break((obj as BreakableComponent).BreakTo); } } /// public override float CalcHeight() { OnBeforeLayout(EventArgs.Empty); // 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.Visible && (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]; DockStyle saveDock = obj.Dock; obj.Dock = DockStyle.None; obj.Height = heights[i]; obj.Top += shifts[i]; if (obj.Visible && obj.Bottom > maxHeight) maxHeight = obj.Bottom; obj.Dock = saveDock; } // perform grow to bottom foreach (ReportComponentBase obj in Objects) { if (obj.GrowToBottom || obj.Bottom > maxHeight) obj.Height = maxHeight - obj.Top; } OnAfterLayout(EventArgs.Empty); return maxHeight; } /// /// This method fires the BeforeLayout event and the script code connected to the BeforeLayoutEvent. /// /// Event data. public void OnBeforeLayout(EventArgs e) { if (BeforeLayout != null) BeforeLayout(this, e); InvokeEvent(BeforeLayoutEvent, e); } /// /// This method fires the AfterLayout event and the script code connected to the AfterLayoutEvent. /// /// Event data. public void OnAfterLayout(EventArgs e) { if (AfterLayout != null) AfterLayout(this, e); InvokeEvent(AfterLayoutEvent, e); } #endregion #region Public methods /// public override void Assign(Base source) { base.Assign(source); ContainerObject src = source as ContainerObject; BeforeLayoutEvent = src.BeforeLayoutEvent; AfterLayoutEvent = src.AfterLayoutEvent; } /// public override void Serialize(FRWriter writer) { ContainerObject c = writer.DiffObject as ContainerObject; base.Serialize(writer); if (writer.SerializeTo == SerializeTo.Preview) return; if (BeforeLayoutEvent != c.BeforeLayoutEvent) writer.WriteStr("BeforeLayoutEvent", BeforeLayoutEvent); if (AfterLayoutEvent != c.AfterLayoutEvent) writer.WriteStr("AfterLayoutEvent", AfterLayoutEvent); } /// public override void Draw(FRPaintEventArgs e) { DrawBackground(e); DrawMarkers(e); Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height)); base.Draw(e); } #endregion /// /// Initializes a new instance of the ContainerObject class with default settings. /// public ContainerObject() { objects = new ReportComponentCollection(this); beforeLayoutEvent = ""; afterLayoutEvent = ""; } } }