using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.ComponentModel; using FastReport.Utils; #if NETSTANDARD || NETCOREAPP using FastReport.Code.CodeDom.Compiler; #else using System.CodeDom.Compiler; #endif using System.Drawing.Design; namespace FastReport { /// /// Specifies a set of actions that cannot be performed on the object in the design mode. /// [Flags] [TypeConverter(typeof(FastReport.TypeConverters.FlagConverter))] public enum Restrictions { /// /// Specifies no restrictions. /// None = 0, /// /// Restricts moving the object. /// DontMove = 1, /// /// Restricts resizing the object. /// DontResize = 2, /// /// Restricts modifying the object's properties. /// DontModify = 4, /// /// Restricts editing the object. /// DontEdit = 8, /// /// Restricts deleting the object. /// DontDelete = 16, /// /// Hides all properties of the object. /// HideAllProperties = 32 } /// /// Specifies a set of actions that can be performed on the object in the design mode. /// [Flags] [TypeConverter(typeof(FastReport.TypeConverters.FlagConverter))] public enum Flags { /// /// Specifies no actions. /// None = 0, /// /// Allows moving the object. /// CanMove = 1, /// /// Allows resizing the object. /// CanResize = 2, /// /// Allows deleting the object. /// CanDelete = 4, /// /// Allows editing the object. /// CanEdit = 8, /// /// Allows changing the Z-order of an object. /// CanChangeOrder = 16, /// /// Allows moving the object to another parent. /// CanChangeParent = 32, /// /// Allows copying the object to the clipboard. /// CanCopy = 64, /// /// Allows drawing the object. /// CanDraw = 128, /// /// Allows grouping the object. /// CanGroup = 256, /// /// Allows write children in the preview mode by itself. /// CanWriteChildren = 512, /// /// Allows write object's bounds into the report stream. /// CanWriteBounds = 1024, /// /// Allows the "smart tag" functionality. /// HasSmartTag = 2048, /// /// Specifies that the object's name is global (this is true for all report objects /// such as Text, Picture and so on). /// HasGlobalName = 4096, /// /// Specifies that the object can display children in the designer's Report Tree window. /// CanShowChildrenInReportTree = 8192, /// /// Specifies that the object supports mouse wheel in the preview window. /// InterceptsPreviewMouseEvents = 16384 } [Flags] internal enum ObjectState : byte { None = 0, IsAncestor = 1, IsDesigning = 2, IsPrinting = 4, IsRunning = 8, IsDeserializing = 16, } /// /// Represents the root class of the FastReport object's hierarchy. /// [ToolboxItem(false)] public abstract partial class Base : Component, IFRSerializable { #region Fields private string name; private Restrictions restrictions; private Flags flags; private Base parent; private string baseName; private ObjectState objectState; private Base originalComponent; private string alias; private Report report; private int zOrder; #endregion #region Properties /// /// Gets or sets the name of the object. /// /// /// Name of the report object must contain alpha, digit, underscore symbols only. /// Data objects such as Variable, TableDataSource /// etc. can have any characters in they names. Each component must have unique /// name. /// /// The following code demonstrates how to find an object by its name: /// /// TextObject text1 = report1.FindObject("Text1") as TextObject; /// /// /// Another object with such name exists. /// Rename an object that was introduced in the ancestor report. [MergableProperty(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Category("Design")] [DisplayName("(Name)")] public virtual string Name { get { return name; } set { if (String.Compare(name, value, true) == 0) return; if (value != "" && Report != null && HasFlag(Flags.HasGlobalName)) { Base c = Report.FindObject(value); if (c != null && c != this) throw new DuplicateNameException(value); if (IsAncestor) throw new AncestorException(name); if (IsDesigning) CheckValidIdent(value); } SetName(value); } } /// /// Gets or sets the flags that restrict some actions in the designer. /// /// /// Use this property to restrict some user actions like move, resize, edit, delete. For example, if /// Restriction.DontMove flag is set, user cannot move the object in the designer. /// [DefaultValue(Restrictions.None)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Category("Design")] [Editor("FastReport.TypeEditors.FlagsEditor, FastReport", typeof(UITypeEditor))] public Restrictions Restrictions { get { return restrictions; } set { restrictions = value; } } /// /// Gets the flags that allow some functionality in the designer. /// /// /// Use this property only if you developing a new FastReport object. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Flags Flags { get { return flags; } } /// /// Gets or sets the parent of the object. /// /// /// Each report object must have a parent in order to appear in the report. Parent must be able to /// contain objects of such type. /// Another way (preferred) to set a parent is to use specific properties of the parent object. /// For example, the object has the collection. /// To add a new page to the report, use the following code: report1.Pages.Add(new ReportPage()); /// /// /// /// Report report1; /// ReportPage page = new ReportPage(); /// page.Parent = report1; /// /// Parent object cannot contain this object. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Base Parent { get { return parent; } set { if (value != parent) { if (value != null) { if (value is IParent) (value as IParent).AddChild(this); } else { (parent as IParent).RemoveChild(this); } } } } /// /// The base part of the object's name. /// /// /// This property is used to automatically create unique object's name. See /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string BaseName { get { return baseName; } set { baseName = value; } } /// /// Gets the short type name. /// /// /// Returns the short type name, such as "TextObject". /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string ClassName { get { return GetType().Name; } } /// /// Gets reference to the parent object. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Report Report { get { if (this is Report) return this as Report; if (report != null) return report; if (Parent != null) return Parent.Report; return null; } } /// /// Gets reference to the parent object. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public PageBase Page { get { if (this is PageBase) return (PageBase)this; Base c = Parent; while (c != null) { if (c is PageBase) return (PageBase)c; c = c.Parent; } return null; } } /// /// Gets the collection of this object's child objects. /// /// /// This property returns child objects that belongs to this object. For example, Report.ChildObjects /// will return only pages that contains in the report, but not page childs such as bands. To return all /// child objects, use property. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ObjectCollection ChildObjects { get { ObjectCollection result = new ObjectCollection(); if (this is IParent) (this as IParent).GetChildObjects(result); return result; } } /// /// Gets the collection of all child objects. /// /// /// This property returns child objects that belongs to this object and to child objects of this object. /// For example, Report.AllObjects will return all objects that contains in the report - such as /// pages, bands, text objects. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ObjectCollection AllObjects { get { ObjectCollection result = new ObjectCollection(); EnumObjects(this, result); return result; } } /// /// Gets or sets the Z-order of the object. /// /// /// The Z-order is also called "creation order". It is the index of an object in the parent's objects list. /// For example, put two text objects on a band. First object will have ZOrder = 0, second = 1. Setting the /// second object's ZOrder to 0 will move it to the back of the first text object. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ZOrder { get { if (parent != null) return (parent as IParent).GetChildOrder(this); return zOrder; } set { if (parent != null) (parent as IParent).SetChildOrder(this, value); else zOrder = value; } } /// /// Gets a value indicating whether the object was introduced in the ancestor report. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsAncestor { get { return GetObjectState(ObjectState.IsAncestor); } } /// /// Gets a value indicating whether the object is in the design state. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsDesigning { get { return GetObjectState(ObjectState.IsDesigning); } } /// /// Gets a value indicating whether the object is currently printing. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsPrinting { get { return GetObjectState(ObjectState.IsPrinting); } } /// /// Gets a value indicating whether the object is currently processed by the report engine. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsRunning { get { return GetObjectState(ObjectState.IsRunning); } } /// /// Gets a value indicating whether the object is currently processed by the report engine. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal bool IsDeserializing { get { return GetObjectState(ObjectState.IsDeserializing); } set { SetObjectState(ObjectState.IsDeserializing, value); } } /// /// Gets an original component for this object. /// /// /// This property is used in the preview mode. Each object in the prepared report is bound to its /// original (from the report template). This technique is used to minimize the prepared report's size. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Base OriginalComponent { get { return originalComponent; } set { originalComponent = value; } } internal string Alias { get { return alias; } set { alias = value; } } #endregion #region Private Methods private void CheckValidIdent(string value) { if (!CodeGenerator.IsValidLanguageIndependentIdentifier(value)) throw new NotValidIdentifierException(value); } private void EnumObjects(Base c, ObjectCollection list) { if (c != this) list.Add(c); foreach (Base obj in c.ChildObjects) EnumObjects(obj, list); } #endregion #region Protected Methods /// /// Helper method, helps to set a reference-type value to the property. /// /// Old property value. /// New property value. /// /// This method is used widely to set a new value to the property that references another FastReport object. /// Method deals with the property. /// /// This is example of the DataBand.Header property: /// public DataHeaderBand Header /// { /// get { return FHeader; } /// set /// { /// SetProp(FHeader, value); /// FHeader = value; /// } /// } /// protected void SetProp(Base prop, Base value) { if (prop != value) { if (prop != null) prop.SetParent(null); if (value != null) { value.Parent = null; value.SetParent(this); } } } /// /// Checks if two float values are different. /// /// First value. /// Second value. /// true if values are not equal. /// /// This method is needed to compare two float values using some precision (0.001). It is useful /// to compare objects' locations and sizes for equality. /// protected bool FloatDiff(float f1, float f2) { return Math.Abs(f1 - f2) > 0.001; } /// /// Deserializes nested object properties. /// /// Reader object. /// /// Typically the object serializes all properties to the single xml item: /// /// <TextObject Name="Text2" Left="18.9" Top="37.8" Width="283.5" Height="28.35"/> /// /// Some objects like have child objects that serialized in subitems: /// /// <DataBand Name="Data1" Top="163" Width="718.2" Height="18.9"> /// <TextObject Name="Text3" Left="18.9" Top="37.8" Width="283.5" Height="28.35"/> /// </DataBand> /// /// To read such subitems, the DeserializeSubItems method is used. Base /// implementation reads the child objects. You may override it to read some specific subitems. /// /// The following code is used to read report's styles: /// /// protected override void DeserializeSubItems(FRReader reader) /// { /// if (String.Compare(reader.ItemName, "Styles", true) == 0) /// reader.Read(Styles); /// else /// base.DeserializeSubItems(reader); /// } /// /// protected virtual void DeserializeSubItems(FRReader reader) { if (reader.ReadChildren) { Base obj = reader.Read() as Base; if (obj != null) { obj.Parent = this; if (IsAncestor && !obj.IsAncestor) obj.ZOrder = obj.zOrder; } } } /// /// Replaces the macros in the given string and returns the new string. /// /// The text containing macros. /// The text with macros replaced with its values. protected string ExtractDefaultMacros(string text) { Dictionary macroValues = Report.PreparedPages.MacroValues; text = ExtractDefaultMacrosInternal(macroValues, text); text = text.Replace("[TOTALPAGES#]", macroValues["TotalPages#"].ToString()); text = text.Replace("[PAGE#]", macroValues["Page#"].ToString()); return text; } /// protected override void Dispose(bool disposing) { if (disposing) { Clear(); Parent = null; } base.Dispose(disposing); } #endregion #region Public Methods /// /// Set object's flags. /// /// Flag to set. /// true to set the flag, false to reset. public void SetFlags(Flags flags, bool value) { if (value) this.flags |= flags; else this.flags &= ~flags; } private bool GetObjectState(ObjectState state) { return (objectState & state) > 0; } private void SetObjectState(ObjectState state, bool value) { if (value) this.objectState |= state; else this.objectState &= ~state; } internal void SetAncestor(bool value) { SetObjectState(ObjectState.IsAncestor, value); } internal void SetDesigning(bool value) { SetObjectState(ObjectState.IsDesigning, value); } internal void SetPrinting(bool value) { SetObjectState(ObjectState.IsPrinting, value); } internal void SetRunning(bool value) { SetObjectState(ObjectState.IsRunning, value); } /// /// Sets the reference to a Report. /// /// Report to set. public void SetReport(Report value) { report = value; } /// /// Sets the object's name. /// /// /// This method is for internal use only. It just sets a new name without any checks /// (unlike the property setter). /// /// Name Property /// New name. public virtual void SetName(string value) { name = value; } /// /// Sets the object's parent. /// /// /// This method is for internal use only. You can use it if you are developing a new /// component for FastReport. Override it to perform some actions when the parent of an /// object is changing. This method checks that parent can contain a child. /// /// Parent object cannot contain this object. /// New parent. public virtual void SetParent(Base value) { if (value != null) if (!(value is IParent) || !(value as IParent).CanContain(this)) throw new ParentException(value, this); SetParentCore(value); } /// /// Sets the object's parent. /// /// New parent. /// /// This method is for internal use only. You can use it if you are developing a new component for FastReport. /// This method does not perform any checks, it just sets the new parent. /// public void SetParentCore(Base value) { parent = value; } /// /// Searches for an object with given name. /// /// Name of the object to find. /// Returns a null reference if object is not found /// The following code demonstrates how to find an object by its name: /// /// TextObject text1 = report1.FindObject("Text1") as TextObject; /// if (text1 != null) /// { /// // object found /// } /// /// public virtual Base FindObject(string name) { ObjectCollection l = AllObjects; foreach (Base c in l) { if (name == c.Name) return c; } return null; } /// /// Creates the unique object's name. /// /// /// Note: you have to set object's parent before calling this method. Method uses the /// property to create a name. /// Note: this method may be very slow on a report that contains lots of objects. Consider /// using own naming logic in this case. /// /// /// /// TextObject textObj = new TextObject(); /// dataBand1.Objects.Add(textObj); /// textObj.CreateUniqueName(); /// /// public void CreateUniqueName() { Report report = Report; if (report == null) return; string s; int i = 1; do { s = baseName + i.ToString(); i++; } while (report.FindObject(s) != null); SetName(s); } /// /// Clears the object's state. /// /// /// This method also disposes all object's children. /// public virtual void Clear() { ObjectCollection list = ChildObjects; foreach (Base c in list) { c.Clear(); c.Dispose(); } } /// /// Serializes the object. /// /// /// Do not call this method directly. You should override it if you are /// developing a new component for FastReport. /// This method is called when the object needs to save the state. It may happen /// when: /// /// /// saving the report to the file or stream; /// /// /// saving the report to the designer's undo buffer; /// /// /// /// assigning the object to another object using the /// or AssignAll methods; /// /// /// /// saving the object to the designer's clipboard; /// /// /// saving the object to the preview (when run a /// report). /// /// /// /// Writer object. public virtual void Serialize(FRWriter writer) { Base c = writer.DiffObject as Base; if (writer.SerializeTo != SerializeTo.Preview) { // in the preview mode we don't need to write ItemName and Name properties. Alias is written instead. writer.ItemName = IsAncestor && (writer.SerializeTo == SerializeTo.Report || writer.SerializeTo == SerializeTo.Undo) ? "inherited" : ClassName; if (Name != "") writer.WriteStr("Name", Name); if (Restrictions != c.Restrictions) writer.WriteValue("Restrictions", Restrictions); if ((writer.SerializeTo == SerializeTo.Report || writer.SerializeTo == SerializeTo.Undo) && !IsAncestor && Parent != null && Parent.IsAncestor) writer.WriteInt("ZOrder", ZOrder); } if (writer.SaveChildren) { foreach (Base child in ChildObjects) { writer.Write(child); } } } /// /// Deserializes the object. /// /// /// Do not call this method directly. You should override it if you are /// developing a new component for FastReport. /// This method is called when the object needs to restore the state. It may /// happen when: /// /// /// loading the report from a file or stream; /// /// /// loading the report from the designer's undo /// buffer; /// /// /// assigning another object to this object using the /// or AssignAll methods; /// /// /// loading the object from the designer's /// clipboard; /// /// loading the object from the preview pages. /// /// /// Reader object. public virtual void Deserialize(FRReader reader) { try { IsDeserializing = true; reader.ReadProperties(this); while (reader.NextItem()) { DeserializeSubItems(reader); } } finally { IsDeserializing = false; } } /// /// Assigns values from another source. /// /// /// Note: this method is relatively slow because it serializes /// an object to the xml and then deserializes it. /// /// Source to assign from. public void BaseAssign(Base source) { bool saveAncestor = source.IsAncestor; source.SetAncestor(false); try { using (XmlItem xml = new XmlItem()) using (FRWriter writer = new FRWriter(xml)) using (FRReader reader = new FRReader(Report, xml)) { writer.SaveChildren = false; writer.Write(source, this); reader.Read(this); } Alias = source.Alias; OriginalComponent = source.OriginalComponent; } finally { source.SetAncestor(saveAncestor); } } /// Copies the contents of another, similar object. /// /// Call Assign to copy the properties from another object of the same type. /// The standard form of a call to Assign is /// destination.Assign(source); /// /// which tells the destination object to copy the contents of the /// source object to itself. In this method, all child objects are /// ignored. If you want to copy child objects, use the /// AssignAll method. /// /// /// /// Report report1; /// Report report2 = new Report(); /// // copy all report settings, do not copy report objects /// report2.Assign(report1); /// /// AssignAll Method /// Source object to copy the contents from. public virtual void Assign(Base source) { Restrictions = source.Restrictions; Alias = source.Alias; OriginalComponent = source.OriginalComponent; } /// Copies the contents (including children) of another, similar object. /// /// /// This method is similar to method. It copies child /// objects as well. /// /// /// /// Report report1; /// Report report2 = new Report(); /// // copy all report settings and objects /// report2.AssignAll(report1); /// /// /// Source object to copy the state from. public void AssignAll(Base source) { AssignAll(source, false); } internal void AssignAll(Base source, bool assignName) { Clear(); Assign(source); if (assignName) SetName(source.Name); foreach (Base child in source.ChildObjects) { Base myChild = Activator.CreateInstance(child.GetType()) as Base; myChild.SetReport(Report); myChild.AssignAll(child, assignName); myChild.SetReport(null); myChild.Parent = this; } } /// /// Gets a value indicating whether the object has the specified parent in its parent hierarchy. /// /// Parent object to check. /// Returns true if the object has given parent in its parent hierarchy. public bool HasParent(Base obj) { Base parent = Parent; while (parent != null) { if (parent == obj) return true; parent = parent.Parent; } return false; } /// /// Gets a value indicating whether the object has a specified flag in its property. /// /// Flag to check. /// true if Flags property contains specified flag. public bool HasFlag(Flags flag) { return (Flags & flag) > 0; } /// /// Gets a value indicating whether the object has a specified restriction /// in its property. /// /// Restriction to check. /// true if Restrictions property contains specified restriction. public bool HasRestriction(Restrictions restriction) { return (Restrictions & restriction) > 0; } /// /// Invokes script event. /// /// Name of the event to invoke. /// Event parameters. /// /// Do not call this method directly. You should use it if you are developing a new component /// for FastReport. /// Use this method to call an event handler that is located in the report's script. /// /// Example of the OnBeforePrint method: /// public void OnBeforePrint(EventArgs e) /// { /// if (BeforePrint != null) /// BeforePrint(this, e); /// InvokeEvent(BeforePrintEvent, e); /// } /// public void InvokeEvent(string name, object param) { if (String.IsNullOrEmpty(name)) return; Report report = Report; if (report != null) report.InvokeMethod(name, new object[] { this, param }); } /// /// Called after all report objects were loaded. /// /// /// Do not call this method directly. You may override it if you are developing a new component /// for FastReport. /// public virtual void OnAfterLoad() { } /// /// Gets all expressions contained in the object. /// /// Array of expressions or null if object contains no expressions. /// /// Do not call this method directly. You may override it if you are developing a /// new component for FastReport. /// /// This method is called by FastReport each time before run a report. FastReport /// do this to collect all expressions and compile them. For example, /// GetExpressions method of the class /// parses the text and returns all expressions found in the text. /// /// public virtual string[] GetExpressions() { return null; } /// /// Returns a custom code that will be added to the report script before report is run. /// /// A custom script text, if any. Otherwise returns null. /// /// This method may return any valid code that may be inserted into the report script. Currently it is /// used in the TableObject to define the following script methods: Sum, Min, Max, Avg, Count. /// /// /// Note: you must take into account the current script language - C# or VB.Net. You may check it via /// Report.ScriptLanguage property. /// /// public virtual string GetCustomScript() { return null; } /// /// Used to extract macros such as "TotalPages#" in the preview mode. /// /// /// This method is used mainly by the TextObject to extract macros and replace it with /// actual values passed in the pageIndex and totalPages parameters. This method /// is called automatically when the object is being previewed. /// public virtual void ExtractMacros() { } /// /// Used to get information of the need to convertation if the function returns true, then the GetConvertedObjects function is called /// /// The export or the object, that call this method /// By default returns false /// /// The functions IsHaveToConvert and GetConvertedObjects allow you to convert objects from one to another, /// for example the export will convert object before adding it to the file and convert recursive, /// i.e. If the new object has the ability to convert, /// it will be converted again but limit is 10 times. /// At the time of export it is called, only on objects inside the band, /// the child objects of converted object will be returned, and the child objects of old object will be ignored. /// public virtual bool IsHaveToConvert(object sender) { return false; } /// /// Used to get an enumeration of the objects to which this object will be converted, before calling this function, the IsHaveToConvert function will be called /// /// By default returns this object /// /// The functions IsHaveToConvert and GetConvertedObjects allow you to convert objects from one to another, /// for example the export will convert object before adding it to the file and convert recursive, /// i.e. If the new object has the ability to convert, /// it will be converted again but limit is 10 times. /// At the time of export it is called, only on objects inside the band, /// the child objects of converted object will be returned, and the child objects of old object will be ignored. /// public virtual IEnumerable GetConvertedObjects() { yield return this; } /// /// Gets the collection of all child objects, converts objects if necessary /// /// the object or export, that call this convertation public ObjectCollection ForEachAllConvectedObjects(object sender) { ObjectCollection list = new ObjectCollection(); EnumAllConvertedObjects(sender, this, list, 0); return list; } private void EnumAllConvertedObjects(object sender, Base c, ObjectCollection list, int convertValue) { if (c != this) { if (c.IsHaveToConvert(sender)) { if (convertValue < 10) { foreach (Base b in c.GetConvertedObjects()) EnumAllConvertedObjects(sender, b, list, convertValue + 1); return; } } list.Add(c); } foreach (Base obj in c.ChildObjects) EnumAllConvertedObjects(sender, obj, list, convertValue); } #endregion /// /// Initializes a new instance of the Base class with default settings. /// public Base() { name = ""; alias = ""; baseName = ClassName; restrictions = new Restrictions(); SetFlags(Flags.CanMove | Flags.CanResize | Flags.CanDelete | Flags.CanEdit | Flags.CanChangeOrder | Flags.CanChangeParent | Flags.CanCopy | Flags.CanDraw | Flags.CanShowChildrenInReportTree, true); } } }