FastReport
392 строки · 13.3 Кб
1using FastReport.Utils;2using System;3using System.ComponentModel;4using System.Drawing;5using System.Windows.Forms;6
7namespace FastReport8{
9/// <summary>10/// Container object that may contain child objects.11/// </summary>12public partial class ContainerObject : ReportComponentBase, IParent13{14#region Fields15private ReportComponentCollection objects;16private bool updatingLayout;17private string beforeLayoutEvent;18private string afterLayoutEvent;19private int savedOriginalObjectsCount;20#endregion21
22#region Properties23/// <summary>24/// Gets the collection of child objects.25/// </summary>26[Browsable(false)]27public ReportComponentCollection Objects28{29get { return objects; }30}31
32/// <summary>33/// This event occurs before the container layouts its child objects.34/// </summary>35public event EventHandler BeforeLayout;36
37/// <summary>38/// This event occurs after the child objects layout was finished.39/// </summary>40public event EventHandler AfterLayout;41
42
43/// <summary>44/// Gets or sets a script event name that will be fired before the container layouts its child objects.45/// </summary>46[Category("Build")]47public string BeforeLayoutEvent48{49get { return beforeLayoutEvent; }50set { beforeLayoutEvent = value; }51}52
53/// <summary>54/// Gets or sets a script event name that will be fired after the child objects layout was finished.55/// </summary>56[Category("Build")]57public string AfterLayoutEvent58{59get { return afterLayoutEvent; }60set { afterLayoutEvent = value; }61}62
63#endregion64
65#region IParent66/// <inheritdoc/>67public virtual void GetChildObjects(ObjectCollection list)68{69foreach (ReportComponentBase c in objects)70{71list.Add(c);72}73}74
75/// <inheritdoc/>76public virtual bool CanContain(Base child)77{78return (child is ReportComponentBase) && (child is not SubreportObject);79}80
81/// <inheritdoc/>82public virtual void AddChild(Base child)83{84if (child is ReportComponentBase)85objects.Add(child as ReportComponentBase);86}87
88/// <inheritdoc/>89public virtual void RemoveChild(Base child)90{91if (child is ReportComponentBase)92objects.Remove(child as ReportComponentBase);93}94
95/// <inheritdoc/>96public virtual int GetChildOrder(Base child)97{98return objects.IndexOf(child as ReportComponentBase);99}100
101/// <inheritdoc/>102public virtual void SetChildOrder(Base child, int order)103{104int oldOrder = child.ZOrder;105if (oldOrder != -1 && order != -1 && oldOrder != order)106{107if (order > objects.Count)108order = objects.Count;109if (oldOrder <= order)110order--;111objects.Remove(child as ReportComponentBase);112objects.Insert(order, child as ReportComponentBase);113}114}115
116/// <inheritdoc/>117public virtual void UpdateLayout(float dx, float dy)118{119if (updatingLayout)120return;121updatingLayout = true;122try123{124RectangleF remainingBounds = new RectangleF(0, 0, Width, Height);125remainingBounds.Width += dx;126remainingBounds.Height += dy;127foreach (ReportComponentBase c in Objects)128{129if ((c.Anchor & AnchorStyles.Right) != 0)130{131if ((c.Anchor & AnchorStyles.Left) != 0)132c.Width += dx;133else134c.Left += dx;135}136else if ((c.Anchor & AnchorStyles.Left) == 0)137{138c.Left += dx / 2;139}140if ((c.Anchor & AnchorStyles.Bottom) != 0)141{142if ((c.Anchor & AnchorStyles.Top) != 0)143c.Height += dy;144else145c.Top += dy;146}147else if ((c.Anchor & AnchorStyles.Top) == 0)148{149c.Top += dy / 2;150}151switch (c.Dock)152{153case DockStyle.Left:154c.Bounds = new RectangleF(remainingBounds.Left, remainingBounds.Top, c.Width, remainingBounds.Height);155remainingBounds.X += c.Width;156remainingBounds.Width -= c.Width;157break;158
159case DockStyle.Top:160c.Bounds = new RectangleF(remainingBounds.Left, remainingBounds.Top, remainingBounds.Width, c.Height);161remainingBounds.Y += c.Height;162remainingBounds.Height -= c.Height;163break;164
165case DockStyle.Right:166c.Bounds = new RectangleF(remainingBounds.Right - c.Width, remainingBounds.Top, c.Width, remainingBounds.Height);167remainingBounds.Width -= c.Width;168break;169
170case DockStyle.Bottom:171c.Bounds = new RectangleF(remainingBounds.Left, remainingBounds.Bottom - c.Height, remainingBounds.Width, c.Height);172remainingBounds.Height -= c.Height;173break;174
175case DockStyle.Fill:176c.Bounds = remainingBounds;177remainingBounds.Width = 0;178remainingBounds.Height = 0;179break;180}181}182}183finally184{185updatingLayout = false;186}187}188#endregion189
190#region Report engine191/// <inheritdoc/>192public override void SaveState()193{194base.SaveState();195savedOriginalObjectsCount = Objects.Count;196SetRunning(true);197SetDesigning(false);198
199foreach (ReportComponentBase obj in Objects)200{201obj.SaveState();202obj.SetRunning(true);203obj.SetDesigning(false);204obj.OnBeforePrint(EventArgs.Empty);205}206}207
208/// <inheritdoc/>209public override void RestoreState()210{211base.RestoreState();212while (Objects.Count > savedOriginalObjectsCount)213{214Objects[Objects.Count - 1].Dispose();215}216SetRunning(false);217
218foreach (ReportComponentBase obj in Objects)219{220obj.OnAfterPrint(EventArgs.Empty);221obj.RestoreState();222obj.SetRunning(false);223}224}225
226/// <inheritdoc/>227public override void GetData()228{229base.GetData();230var objArray = Objects.ToArray();231foreach (ReportComponentBase obj in objArray)232{233obj.GetData();234obj.OnAfterData();235
236// break the component if it is of BreakableComponent an has non-empty BreakTo property237if (obj is BreakableComponent && (obj as BreakableComponent).BreakTo != null &&238(obj as BreakableComponent).BreakTo.GetType() == obj.GetType())239(obj as BreakableComponent).Break((obj as BreakableComponent).BreakTo);240}241}242
243/// <inheritdoc/>244public override float CalcHeight()245{246OnBeforeLayout(EventArgs.Empty);247
248// sort objects by Top249ReportComponentCollection sortedObjects = Objects.SortByTop();250
251// calc height of each object252float[] heights = new float[sortedObjects.Count];253for (int i = 0; i < sortedObjects.Count; i++)254{255ReportComponentBase obj = sortedObjects[i];256float height = obj.Height;257if (obj.Visible && (obj.CanGrow || obj.CanShrink))258{259float height1 = obj.CalcHeight();260if ((obj.CanGrow && height1 > height) || (obj.CanShrink && height1 < height))261height = height1;262}263heights[i] = height;264}265
266// calc shift amounts267float[] shifts = new float[sortedObjects.Count];268for (int i = 0; i < sortedObjects.Count; i++)269{270ReportComponentBase parent = sortedObjects[i];271float shift = heights[i] - parent.Height;272if (shift == 0)273continue;274
275for (int j = i + 1; j < sortedObjects.Count; j++)276{277ReportComponentBase child = sortedObjects[j];278if (child.ShiftMode == ShiftMode.Never)279continue;280
281if (child.Top >= parent.Bottom - 1e-4)282{283if (child.ShiftMode == ShiftMode.WhenOverlapped &&284(child.Left > parent.Right - 1e-4 || parent.Left > child.Right - 1e-4))285continue;286
287float parentShift = shifts[i];288float childShift = shifts[j];289if (shift > 0)290childShift = Math.Max(shift + parentShift, childShift);291else292childShift = Math.Min(shift + parentShift, childShift);293shifts[j] = childShift;294}295}296}297
298// update location and size of each component, calc max height299float maxHeight = 0;300for (int i = 0; i < sortedObjects.Count; i++)301{302ReportComponentBase obj = sortedObjects[i];303DockStyle saveDock = obj.Dock;304obj.Dock = DockStyle.None;305obj.Height = heights[i];306obj.Top += shifts[i];307if (obj.Visible && obj.Bottom > maxHeight)308maxHeight = obj.Bottom;309obj.Dock = saveDock;310}311
312// perform grow to bottom313foreach (ReportComponentBase obj in Objects)314{315if (obj.GrowToBottom || obj.Bottom > maxHeight)316obj.Height = maxHeight - obj.Top;317}318
319OnAfterLayout(EventArgs.Empty);320return maxHeight;321}322
323/// <summary>324/// This method fires the <b>BeforeLayout</b> event and the script code connected to the <b>BeforeLayoutEvent</b>.325/// </summary>326/// <param name="e">Event data.</param>327public void OnBeforeLayout(EventArgs e)328{329if (BeforeLayout != null)330BeforeLayout(this, e);331InvokeEvent(BeforeLayoutEvent, e);332}333
334/// <summary>335/// This method fires the <b>AfterLayout</b> event and the script code connected to the <b>AfterLayoutEvent</b>.336/// </summary>337/// <param name="e">Event data.</param>338public void OnAfterLayout(EventArgs e)339{340if (AfterLayout != null)341AfterLayout(this, e);342InvokeEvent(AfterLayoutEvent, e);343}344#endregion345
346#region Public methods347/// <inheritdoc/>348public override void Assign(Base source)349{350base.Assign(source);351
352ContainerObject src = source as ContainerObject;353BeforeLayoutEvent = src.BeforeLayoutEvent;354AfterLayoutEvent = src.AfterLayoutEvent;355}356
357/// <inheritdoc/>358public override void Serialize(FRWriter writer)359{360ContainerObject c = writer.DiffObject as ContainerObject;361base.Serialize(writer);362
363if (writer.SerializeTo == SerializeTo.Preview)364return;365
366if (BeforeLayoutEvent != c.BeforeLayoutEvent)367writer.WriteStr("BeforeLayoutEvent", BeforeLayoutEvent);368if (AfterLayoutEvent != c.AfterLayoutEvent)369writer.WriteStr("AfterLayoutEvent", AfterLayoutEvent);370}371
372/// <inheritdoc/>373public override void Draw(FRPaintEventArgs e)374{375DrawBackground(e);376DrawMarkers(e);377Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height));378base.Draw(e);379}380#endregion381
382/// <summary>383/// Initializes a new instance of the <b>ContainerObject</b> class with default settings.384/// </summary>385public ContainerObject()386{387objects = new ReportComponentCollection(this);388beforeLayoutEvent = "";389afterLayoutEvent = "";390}391}392}
393