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 = "";
}
}
}