using FastReport.Code;
using FastReport.CrossView;
using FastReport.Data;
using FastReport.Dialog;
using FastReport.Engine;
using FastReport.Export;
using FastReport.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FastReport
{
///
/// Specifies the language of the report's script.
///
public enum Language
{
///
/// The C# language.
///
CSharp,
///
/// The VisualBasic.Net language.
///
Vb
}
///
/// Specifies the quality of text rendering.
///
public enum TextQuality
{
///
/// The default text quality, depends on system settings.
///
Default,
///
/// The regular quality.
///
Regular,
///
/// The "ClearType" quality.
///
ClearType,
///
/// The AntiAlias quality. This mode may be used to produce the WYSIWYG text.
///
AntiAlias,
///
/// The "SingleBitPerPixel" quality.
///
SingleBPP,
///
/// The "SingleBitPerPixelGridFit" quality.
///
SingleBPPGridFit
}
///
/// Specifies the report operation.
///
public enum ReportOperation
{
///
/// Specifies no operation.
///
None,
///
/// The report is running.
///
Running,
///
/// The report is printing.
///
Printing,
///
/// The report is exporting.
///
Exporting
}
///
/// Specifies the page range to print/export.
///
public enum PageRange
{
///
/// Print all pages.
///
All,
///
/// Print current page.
///
Current,
///
/// Print pages specified in the PageNumbers property of the PrintSettings.
///
PageNumbers
}
public class ReportLogEventArgs : EventArgs
{
public bool IsError { get; private set; }
public string Message { get; private set; }
public ReportLogEventArgs(bool iserror, string message)
{
IsError = iserror;
Message = message;
}
}
public delegate void ReportLogEvent(object sender, ReportLogEventArgs args);
///
/// Represents a report object.
///
///
/// The instance of this class contains a report. Here are some common
/// actions that can be performed with this object:
///
/// -
/// To load a report, use the
/// method or call static method.
///
/// -
/// To save a report, call the method.
///
/// -
/// To register application dataset for use it in a report, call one of the
/// RegisterData methods.
///
/// -
/// To pass some parameter to a report, use the
/// method.
///
/// -
/// To design a report, call the method.
///
/// -
/// To run a report and preview it, call the method.
/// Another way is to call the method, then call the
/// method.
///
/// -
/// To run a report and print it, call the method.
/// Another way is to call the method, then call the
/// method.
///
/// -
/// To load/save prepared report, use one of the LoadPrepared and
/// SavePrepared methods.
///
/// -
/// To set up some global properties, use the static class
/// or component that you can use in the Visual Studio IDE.
///
///
///
/// The report consists of one or several report pages (pages of the
/// type) and/or dialog forms (pages of the type).
/// They are stored in the collection. In turn, each page may contain report
/// objects. See the example below how to create a simple report in code.
///
/// This example shows how to create a report instance, load it from a file,
/// register the application data, run and preview.
///
/// Report report = new Report();
/// report.Load("reportfile.frx");
/// report.RegisterData(application_dataset);
/// report.Show();
///
/// This example shows how to create simple report in code.
///
/// Report report = new Report();
/// // create the report page
/// ReportPage page = new ReportPage();
/// page.Name = "ReportPage1";
/// // set paper width and height. Note: these properties are measured in millimeters.
/// page.PaperWidth = 210;
/// page.PaperHeight = 297;
/// // add a page to the report
/// report.Pages.Add(page);
/// // create report title
/// page.ReportTitle = new ReportTitleBand();
/// page.ReportTitle.Name = "ReportTitle1";
/// page.ReportTitle.Height = Units.Millimeters * 10;
/// // create Text object and put it to the title
/// TextObject text = new TextObject();
/// text.Name = "Text1";
/// text.Bounds = new RectangleF(0, 0, Units.Millimeters * 100, Units.Millimeters * 5);
/// page.ReportTitle.Objects.Add(text);
/// // create data band
/// DataBand data = new DataBand();
/// data.Name = "Data1";
/// data.Height = Units.Millimeters * 10;
/// // add data band to a page
/// page.Bands.Add(data);
///
///
public partial class Report : Base, IParent, ISupportInitialize
{
#region Fields
private PageCollection pages;
private Dictionary dictionary;
private ReportInfo reportInfo;
private string baseReport;
private Report baseReportObject;
private string baseReportAbsolutePath;
private string fileName;
private string scriptText;
private Language scriptLanguage;
private bool compressed;
private bool useFileCache;
private TextQuality textQuality;
private bool smoothGraphics;
private string password;
private bool convertNulls;
private bool doublePass;
private bool autoFillDataSet;
private int initialPageNumber;
private int maxPages;
private string startReportEvent;
private string finishReportEvent;
private StyleCollection styles;
private CodeHelperBase codeHelper;
private GraphicCache graphicCache;
private string[] referencedAssemblies;
private Hashtable cachedDataItems;
private AssemblyCollection assemblies;
private FastReport.Preview.PreparedPages preparedPages;
private ReportEngine engine;
private bool aborted;
private Bitmap measureBitmap;
private IGraphics measureGraphics;
private bool storeInResources;
private PermissionSet scriptRestrictions;
private ReportOperation operation;
private bool needCompile;
private bool scriptChanged = false;
private bool needRefresh;
private bool isParameterChanged = false;
private bool initializing;
private object initializeData;
private string initializeDataName;
private object tag;
private bool isLoadPrepared = false;
#endregion Fields
#region Properties
///
/// Occurs when calc execution is started.
///
public event CustomCalcEventHandler CustomCalc;
///
/// Occurs when report is inherited and trying to load a base report.
///
///
/// Typical use of this event is to load the base report from a database instead of a file.
///
public event CustomLoadEventHandler LoadBaseReport;
///
/// Occurs when report execution is started.
///
public event EventHandler StartReport;
///
/// Occurs when report execution is finished.
///
public event EventHandler FinishReport;
///
/// Occurs before export to set custom export parameters.
///
public event EventHandler ExportParameters;
///
/// Gets the pages contained in this report.
///
///
/// This property contains pages of all types (report and dialog). Use the is/as operators
/// if you want to work with pages of ReportPage type.
///
/// The following code demonstrates how to access the first report page:
///
/// ReportPage page1 = report1.Pages[0] as ReportPage;
///
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public PageCollection Pages
{
get { return pages; }
}
///
/// Gets the report's data.
///
///
/// The dictionary contains all data items such as connections, data sources, parameters,
/// system variables.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary Dictionary
{
get { return dictionary; }
set
{
SetProp(dictionary, value);
dictionary = value;
}
}
///
/// Gets the collection of report parameters.
///
///
/// Parameters are displayed in the "Data" window under the "Parameters" node.
/// Typical use of parameters is to pass some static data from the application to the report.
/// You can print such data, use it in the data row filter, script etc.
/// Another way to use parameters is to define some reusable piece of code, for example,
/// to define an expression that will return the concatenation of first and second employee name.
/// In this case, you set the parameter's Expression property to something like this:
/// [Employees.FirstName] + " " + [Employees.LastName]. Now this parameter may be used in the report
/// to print full employee name. Each time you access such parameter, it will calculate the expression
/// and return its value.
/// You can create nested parameters. To do this, add the new Parameter to the
/// Parameters collection of the root parameter. To access the nested parameter, you may use the
/// method.
/// To get or set the parameter's value, use the and
/// methods. To set the parameter's expression, use the
/// method that returns a Parameter object and set its
/// Expression property.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ParameterCollection Parameters
{
get { return dictionary.Parameters; }
}
///
/// Gets or sets the report information such as report name, author, description etc.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Design")]
public ReportInfo ReportInfo
{
get { return reportInfo; }
set { reportInfo = value; }
}
///
/// Gets or sets the base report file name.
///
///
/// This property contains the name of a report file this report is inherited from.
/// Note: setting this property to non-empty value will clear the report and
/// load the base file into it.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string BaseReport
{
get { return baseReport; }
set { SetBaseReport(value); }
}
///
/// Gets a value indicating whether Report is prepared
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsPrepared
{
get { return !isParameterChanged && PreparedPages != null && PreparedPages.Count != 0; }
}
///
/// Gets or sets the absolute path to the parent report.
///
///
/// This property contains the absolute path to the parent report.
///
[Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string BaseReportAbsolutePath
{
get { return baseReportAbsolutePath; }
set { baseReportAbsolutePath = value; }
}
///
/// Gets or sets the name of a file the report was loaded from.
///
///
/// This property is used to support the FastReport.Net infrastructure;
/// typically you don't need to use it.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string FileName
{
get { return fileName; }
set { fileName = value; }
}
///
/// Gets or sets the report script.
///
///
/// The script contains the ReportScript class that contains all report objects'
/// event handlers and own items such as private fields, properties, methods etc. The script
/// contains only items written by you. Unlike other report generators, the script does not
/// contain report objects declarations, initialization code. It is added automatically when
/// you run the report.
/// By default this property contains an empty script text. You may see it in the designer
/// when you switch to the Code window.
/// If you set this property programmatically, you have to declare the FastReport
/// namespace and the ReportScript class in it. Do not declare report items (such as bands,
/// objects, etc) in the ReportScript class: the report engine does this automatically when
/// you run the report.
/// Security note: since the report script is compiled into .NET assembly, it allows
/// you to do ANYTHING. For example, you may create a script that will read/write files from/to a disk.
/// To restrict such operations, use the property.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string ScriptText
{
get { return scriptText; }
set
{
scriptText = value;
scriptChanged = scriptText != codeHelper.EmptyScript();
}
}
///
/// Gets or sets the script language of this report.
///
///
/// Note: changing this property will reset the report script to default empty script.
///
[DefaultValue(Language.CSharp)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Script")]
public Language ScriptLanguage
{
get { return scriptLanguage; }
set
{
bool needClear = scriptLanguage != value;
scriptLanguage = value;
if (scriptLanguage == Language.CSharp)
codeHelper = new CsCodeHelper(this);
else
codeHelper = new VbCodeHelper(this);
if (needClear)
{
scriptText = codeHelper.EmptyScript();
scriptChanged = false;
}
}
}
///
/// Gets or sets a value indicating whether the null DB value must be converted to zero, false or
/// empty string depending on the data column type.
///
///
/// This property is true by default. If you set it to false, you should check
/// the DB value before you do something with it (for example, typecast it to any type, use it
/// in a expression etc.)
///
[DefaultValue(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Engine")]
public bool ConvertNulls
{
get { return convertNulls; }
set { convertNulls = value; }
}
///
/// Gets or sets a value that specifies whether the report engine should perform the second pass.
///
///
/// Typically the second pass is necessary to print the number of total pages. It also
/// may be used to perform some calculations on the first pass and print its results on the
/// second pass.
/// Use the Engine.FirstPass, Engine.FinalPass properties to determine which
/// pass the engine is performing now.
///
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Engine")]
public bool DoublePass
{
get { return doublePass; }
set { doublePass = value; }
}
///
/// Gets or sets a value that specifies whether to compress the report file.
///
///
/// The report file is compressed using the Gzip algorithm. So you can open the
/// compressed report in any zip-compatible archiver.
///
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Misc")]
public bool Compressed
{
get { return compressed; }
set { compressed = value; }
}
///
/// Returns a bool value depending on the .frx or .fpx report was loaded
///
public bool IsLoadPrepared
{
get => isLoadPrepared;
}
///
/// Gets or sets a value that specifies whether to use the file cache rather than memory
/// to store the prepared report pages.
///
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Engine")]
public bool UseFileCache
{
get { return useFileCache; }
set { useFileCache = value; }
}
///
/// Gets or sets a value that specifies the quality of text rendering.
///
///
/// Note: the default property value is TextQuality.Default. That means the report
/// may look different depending on OS settings. This property does not affect the printout.
///
[DefaultValue(TextQuality.Default)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Misc")]
public TextQuality TextQuality
{
get { return textQuality; }
set { textQuality = value; }
}
///
/// Gets or sets a value that specifies if the graphic objects such as bitmaps
/// and shapes should be displayed smoothly.
///
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Misc")]
public bool SmoothGraphics
{
get { return smoothGraphics; }
set { smoothGraphics = value; }
}
///
/// Gets or sets the report password.
///
///
/// When you try to load the password-protected report, you will be asked
/// for a password. You also may specify the password in this property before loading
/// the report. In this case the report will load silently.
/// Password-protected report file is crypted using Rijndael algorithm.
/// Do not forget your password! It will be hard or even impossible to open
/// the protected file in this case.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Password
{
get { return password; }
set { password = value; }
}
///
/// Gets or sets a value indicating whether it is necessary to automatically fill
/// DataSet registered with RegisterData call.
///
///
/// If this property is true (by default), FastReport will automatically fill
/// the DataSet with data when you trying to run a report. Set it to false if
/// you want to fill the DataSet by yourself.
///
[DefaultValue(true)]
[SRCategory("Misc")]
public bool AutoFillDataSet
{
get { return autoFillDataSet; }
set { autoFillDataSet = value; }
}
///
/// Gets or sets the maximum number of generated pages in a prepared report.
///
///
/// Use this property to limit the number of pages in a prepared report.
///
[DefaultValue(0)]
[SRCategory("Misc")]
public int MaxPages
{
get { return maxPages; }
set { maxPages = value; }
}
///
/// Gets or sets the collection of styles used in this report.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Misc")]
public StyleCollection Styles
{
get { return styles; }
set { styles = value; }
}
///
/// Gets or sets an array of assembly names that will be used to compile the report script.
///
///
/// By default this property contains the following assemblies: "System.dll", "System.Drawing.dll",
/// "System.Windows.Forms.dll", "System.Data.dll", "System.Xml.dll". If your script uses some types
/// from another assemblies, you have to add them to this property.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Script")]
public string[] ReferencedAssemblies
{
get { return referencedAssemblies; }
set
{
if (value != null)
{
// fix for old reports with "System.Windows.Forms.DataVisualization" in referenced assemblies
for (int i = 0; i < value.Length; i++)
{
value[i] = value[i].Replace("System.Windows.Forms.DataVisualization", "FastReport.DataVisualization");
}
}
referencedAssemblies = value;
}
}
///
/// Gets or sets a script event name that will be fired when the report starts.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Build")]
public string StartReportEvent
{
get { return startReportEvent; }
set { startReportEvent = value; }
}
///
/// Gets or sets a script event name that will be fired when the report is finished.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRCategory("Build")]
public string FinishReportEvent
{
get { return finishReportEvent; }
set { finishReportEvent = value; }
}
///
/// Gets a value indicating that report execution was aborted.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool Aborted
{
get
{
Config.DoEvent();
return aborted;
}
}
///
/// Gets or sets a value that determines whether to store the report in the application resources.
/// Use this property in the MS Visual Studio IDE only.
///
///
/// By default this property is true. When set to false, you should store your report
/// in a file.
///
[DefaultValue(true)]
[SRCategory("Design")]
public bool StoreInResources
{
get { return storeInResources; }
set { storeInResources = value; }
}
///
/// Gets or sets the resource string that contains the report.
///
///
/// This property is used by the MS Visual Studio to store the report. Do not use it directly.
///
[Browsable(false)]
[Localizable(true)]
public string ReportResourceString
{
get
{
if (!StoreInResources)
return "";
return SaveToString();
}
set
{
if (String.IsNullOrEmpty(value))
{
Clear();
return;
}
LoadFromString(value);
}
}
///
/// Gets a value indicating that this report contains dialog forms.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool HasDialogs
{
get
{
foreach (PageBase page in Pages)
{
if (page is DialogPage)
return true;
}
return false;
}
}
///
/// Gets or sets a set of permissions that will be restricted for the script code.
///
///
/// Since the report script is compiled into .NET assembly, it allows you to do ANYTHING.
/// For example, you may create a script that will read/write files from/to a disk. This property
/// is used to restrict such operations.
/// This example shows how to restrict the file IO operations in a script:
///
/// using System.Security;
/// using System.Security.Permissions;
/// ...
/// PermissionSet ps = new PermissionSet(PermissionState.None);
/// ps.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
/// report1.ScriptRestrictions = ps;
/// report1.Prepare();
///
///
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public PermissionSet ScriptRestrictions
{
get { return scriptRestrictions; }
set { scriptRestrictions = value; }
}
///
/// Gets a reference to the graphics cache for this report.
///
///
/// This property is used to support the FastReport.Net infrastructure. Do not use it directly.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public GraphicCache GraphicCache
{
get { return graphicCache; }
}
///
/// Gets a pages of the prepared report.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Preview.PreparedPages PreparedPages
{
get { return preparedPages; }
}
///
/// Gets a reference to the report engine.
///
///
/// This property can be used when report is running. In other cases it returns null.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ReportEngine Engine
{
get { return engine; }
}
///
/// Gets or sets the initial page number for PageN/PageNofM system variables.
///
[DefaultValue(1)]
[SRCategory("Engine")]
public int InitialPageNumber
{
get { return initialPageNumber; }
set { initialPageNumber = value; }
}
///
/// This property is not relevant to this class.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new string Name
{
get { return base.Name; }
}
///
/// This property is not relevant to this class.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Restrictions Restrictions
{
get { return base.Restrictions; }
set { base.Restrictions = value; }
}
///
/// Gets the report operation that is currently performed.
///
[Browsable(false)]
public ReportOperation Operation
{
get { return operation; }
}
///
/// Gets or sets the Tag object of the report.
///
[Browsable(false)]
public object Tag
{
get { return tag; }
set { tag = value; }
}
private string[] DefaultAssemblies
{
get
{
return new string[] {
"System.dll",
"System.Drawing.dll",
"System.Data.dll",
"System.Xml.dll",
"FastReport.Compat.dll",
#if !CROSSPLATFORM && !(WPF || AVALONIA)
"System.Windows.Forms.dll",
#endif
#if WPF
"FastReport.Forms.WPF.dll",
#endif
#if AVALONIA
"FastReport.Forms.Avalonia.dll",
#endif
#if CROSSPLATFORM || COREWIN
"System.Drawing.Primitives",
#endif
#if MSCHART
"FastReport.DataVisualization.dll"
#endif
};
}
}
internal CodeHelperBase CodeHelper
{
get { return codeHelper; }
}
public IGraphics MeasureGraphics
{
get
{
if (measureGraphics == null)
{
#if CROSSPLATFORM || MONO
measureBitmap = new Bitmap(1, 1);
measureGraphics = new GdiGraphics(measureBitmap);
#else
measureGraphics = GdiGraphics.FromGraphics(Graphics.FromHwnd(IntPtr.Zero));
#endif
}
return measureGraphics;
}
}
public string GetReportName
{
get
{
string result = ReportInfo.Name;
if (String.IsNullOrEmpty(result))
result = Path.GetFileNameWithoutExtension(FileName);
return result;
}
}
///
/// Gets or sets the flag for refresh.
///
public bool NeedRefresh
{
get { return needRefresh; }
set { needRefresh = value; }
}
internal ObjectCollection AllNamedObjects
{
get
{
ObjectCollection allObjects = AllObjects;
// data objects are not included into AllObjects list. Include named items separately.
foreach (Base c in Dictionary.AllObjects)
{
if (c is DataConnectionBase || c is DataSourceBase || c is Relation || c is CubeSourceBase)
allObjects.Add(c);
}
return allObjects;
}
}
#endregion Properties
#region Logging
public event ReportLogEvent Log;
public void DoLog(bool iserror, string message) => Log?.Invoke(this, new ReportLogEventArgs(iserror, message));
#endregion`
#region Private Methods
private bool ShouldSerializeReferencedAssemblies()
{
return Converter.ToString(ReferencedAssemblies) != Converter.ToString(DefaultAssemblies);
}
// convert absolute path to the base report to relative path (based on the main report path).
private string GetRelativePathToBaseReport()
{
string path = "";
if (!String.IsNullOrEmpty(FileName))
{
try
{
path = Path.GetDirectoryName(FileName);
}
catch
{
}
}
if (!String.IsNullOrEmpty(path))
{
try
{
return FileUtils.GetRelativePath(BaseReport, path);
}
catch
{
}
}
return BaseReport;
}
private void SetBaseReport(string value)
{
baseReport = value;
if (baseReportObject != null)
{
baseReportObject.Dispose();
baseReportObject = null;
}
// detach the base report
if (String.IsNullOrEmpty(value))
{
foreach (Base c in AllObjects)
{
c.SetAncestor(false);
}
SetAncestor(false);
return;
}
string saveFileName = fileName;
if (LoadBaseReport != null)
{
LoadBaseReport(this, new CustomLoadEventArgs(value, this));
}
else
{
// convert the relative path to absolute path (based on the main report path).
if (!Path.IsPathRooted(value))
{
var fullPath = Path.GetFullPath(Path.GetDirectoryName(FileName));
// since directory path separator for Win OS is '\' and for Unix OS is '/'
// we have to modify the incoming path string with actual for current OS path separator
value = Path.Combine(fullPath, GetFixedSeparatedPath(value));
}
if (!File.Exists(value) && File.Exists(BaseReportAbsolutePath))
{
value = BaseReportAbsolutePath;
}
Load(value);
}
fileName = saveFileName;
baseReport = "";
Password = "";
baseReportObject = Activator.CreateInstance(GetType()) as Report;
baseReportObject.AssignAll(this, true);
// set Ancestor & CanChangeParent flags
foreach (Base c in AllObjects)
{
c.SetAncestor(true);
}
SetAncestor(true);
baseReport = value;
}
private static string GetFixedSeparatedPath(string baseReport)
{
return baseReport.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
}
private void GetDiff(object sender, DiffEventArgs e)
{
if (baseReportObject != null)
{
if (e.Object is Report)
e.DiffObject = baseReportObject;
else if (e.Object is Base)
e.DiffObject = baseReportObject.FindObject((e.Object as Base).Name);
}
}
private void ClearReportProperties()
{
ReportInfo.Clear();
Dictionary.Clear();
if (IsDesigning)
{
ScriptLanguage = Config.ReportSettings.DefaultLanguage;
}
else
{
ScriptLanguage = Language.CSharp;
}
// not property, only field!
scriptText = codeHelper.EmptyScript();
scriptChanged = false;
BaseReport = "";
BaseReportAbsolutePath = "";
DoublePass = false;
ConvertNulls = true;
Compressed = false;
TextQuality = TextQuality.Default;
SmoothGraphics = false;
Password = "";
InitialPageNumber = 1;
MaxPages = 0;
ClearDesign();
Styles.Clear();
Styles.Name = "";
referencedAssemblies = DefaultAssemblies;
StartReportEvent = "";
FinishReportEvent = "";
#if REFLECTION_EMIT_COMPILER
_cachedParsedExpressions.Clear();
#endif
needCompile = true;
}
#endregion Private Methods
#region Protected Methods
///
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (graphicCache != null)
graphicCache.Dispose();
graphicCache = null;
if (measureGraphics != null)
measureGraphics.Dispose();
measureGraphics = null;
if (measureBitmap != null)
measureBitmap.Dispose();
measureBitmap = null;
DisposeDesign();
if (PreparedPages != null)
PreparedPages.Dispose();
}
base.Dispose(disposing);
}
///
protected override void DeserializeSubItems(FRReader reader)
{
if (String.Compare(reader.ItemName, "ScriptText", true) == 0)
ScriptText = reader.ReadPropertyValue();
else if (String.Compare(reader.ItemName, "Dictionary", true) == 0)
reader.Read(Dictionary);
else if (String.Compare(reader.ItemName, "Styles", true) == 0)
reader.Read(Styles);
else
base.DeserializeSubItems(reader);
}
#endregion Protected Methods
#region IParent
///
public bool CanContain(Base child)
{
return child is PageBase || child is Dictionary;
}
///
public void GetChildObjects(ObjectCollection list)
{
foreach (PageBase page in pages)
{
list.Add(page);
}
}
///
public void AddChild(Base obj)
{
if (obj is PageBase)
pages.Add(obj as PageBase);
else if (obj is Dictionary)
Dictionary = obj as Dictionary;
}
///
public void RemoveChild(Base obj)
{
if (obj is PageBase)
pages.Remove(obj as PageBase);
else if (obj is Dictionary && (obj as Dictionary) == dictionary)
Dictionary = null;
}
///
public virtual int GetChildOrder(Base child)
{
if (child is PageBase)
return pages.IndexOf(child as PageBase);
return 0;
}
///
public virtual void SetChildOrder(Base child, int order)
{
if (child is PageBase)
{
if (order > pages.Count)
order = pages.Count;
int oldOrder = child.ZOrder;
if (oldOrder != -1 && order != -1 && oldOrder != order)
{
if (oldOrder <= order)
order--;
pages.Remove(child as PageBase);
pages.Insert(order, child as PageBase);
}
}
}
///
public virtual void UpdateLayout(float dx, float dy)
{
// do nothing
}
#endregion IParent
#region ISupportInitialize Members
///
public void BeginInit()
{
initializing = true;
}
///
public void EndInit()
{
initializing = false;
Dictionary.RegisterData(initializeData, initializeDataName, false);
}
#endregion ISupportInitialize Members
#region Script related
private void FillDataSourceCache()
{
cachedDataItems.Clear();
ObjectCollection dictionaryObjects = Dictionary.AllObjects;
foreach (Parameter c in Dictionary.SystemVariables)
{
dictionaryObjects.Add(c);
}
foreach (Base c in dictionaryObjects)
{
if (c is DataSourceBase)
{
DataSourceBase data = c as DataSourceBase;
CachedDataItem cachedItem = new CachedDataItem();
cachedItem.dataSource = data;
cachedDataItems[data.FullName] = cachedItem;
for (int i = 0; i < data.Columns.Count; i++)
{
cachedItem = new CachedDataItem();
cachedItem.dataSource = data;
cachedItem.column = data.Columns[i];
cachedDataItems[data.FullName + "." + data.Columns[i].Alias] = cachedItem;
}
}
else if (c is Parameter)
{
cachedDataItems[(c as Parameter).FullName] = c;
}
else if (c is Total)
{
cachedDataItems[(c as Total).Name] = c;
}
}
}
internal void Compile()
{
FillDataSourceCache();
#if REFLECTION_EMIT_COMPILER
if (Config.CompilerSettings.ReflectionEmitCompiler)
{
SetIsCompileNeeded();
if (!IsCompileNeeded)
return;
}
#endif
if (needCompile)
{
Debug.WriteLine("Compile...");
using (AssemblyDescriptor descriptor = new AssemblyDescriptor(this, ScriptText))
{
assemblies.Clear();
assemblies.Add(descriptor);
descriptor.AddObjects();
descriptor.AddExpressions();
descriptor.AddFunctions();
descriptor.Compile();
}
}
else
{
InternalInit();
}
}
#if ASYNC
internal async Task CompileAsync(CancellationToken token)
{
FillDataSourceCache();
#if REFLECTION_EMIT_COMPILER
if (Config.CompilerSettings.ReflectionEmitCompiler)
{
SetIsCompileNeeded();
if (!IsCompileNeeded)
return;
}
#endif
if (needCompile)
{
AssemblyDescriptor descriptor = new AssemblyDescriptor(this, ScriptText);
assemblies.Clear();
assemblies.Add(descriptor);
descriptor.AddObjects();
descriptor.AddExpressions();
descriptor.AddFunctions();
await descriptor.CompileAsync(token);
}
else
{
InternalInit();
}
}
#endif
///
/// Initializes the report's fields.
///
///
/// This method is for internal use only.
///
protected void InternalInit()
{
needCompile = false;
AssemblyDescriptor descriptor = new AssemblyDescriptor(this, CodeHelper.EmptyScript());
assemblies.Clear();
assemblies.Add(descriptor);
descriptor.InitInstance(this);
}
///
/// Generates the file (.cs or .vb) that contains the report source code.
///
/// Name of the file.
///
/// Use this method to generate the report source code. This code can be attached to your project.
/// In this case, you will need to call the following code to run a report:
///
/// SimpleListReport report = new SimpleListReport();
/// report.RegisterData(your_dataset);
/// report.Show();
///
///
public void GenerateReportAssembly(string fileName)
{
// create the class name
string className = "";
const string punctuation = " ~`!@#$%^&*()-=+[]{},.<>/?;:'\"\\|";
foreach (char c in Path.GetFileNameWithoutExtension(fileName))
{
if (!punctuation.Contains(c.ToString()))
className += c;
}
AssemblyDescriptor descriptor = new AssemblyDescriptor(this, ScriptText);
descriptor.AddObjects();
descriptor.AddExpressions();
descriptor.AddFunctions();
string reportClassText = descriptor.GenerateReportClass(className);
File.WriteAllText(fileName, reportClassText, Encoding.UTF8);
}
///
/// Calculates an expression and returns the result.
///
/// The expression to calculate.
/// If report is running, returns the result of calculation.
/// Otherwise returns null.
///
/// The expression may be any valid expression such as "1 + 2". The expression
/// is calculated in the report script's ReportScript class instance context,
/// so you may refer to any objects available in this context: private fields,
/// methods, report objects.
///
public object Calc(string expression)
{
return Calc(expression, 0);
}
///
/// Calculates an expression and returns the result.
///
/// The expression to calculate.
/// The value of currently printing object.
/// If report is running, returns the result of calculation.
/// Otherwise returns null.
///
/// Do not call this method directly. Use the Calc(string expression) method instead.
///
public object Calc(string expression, Variant value)
{
if (!IsRunning)
return null;
if (String.IsNullOrEmpty(expression) || String.IsNullOrEmpty(expression.Trim()))
return null;
string expr = expression;
if (expr.StartsWith("[") && expr.EndsWith("]"))
expr = expression.Substring(1, expression.Length - 2);
// check cached items first
object cachedObject = cachedDataItems[expr];
if (cachedObject is CachedDataItem)
{
CachedDataItem cachedItem = cachedObject as CachedDataItem;
DataSourceBase data = cachedItem.dataSource;
Column column = cachedItem.column;
object val = ConvertToColumnDataType(column.Value, column.DataType, ConvertNulls);
if (CustomCalc != null)
{
CustomCalcEventArgs e = new CustomCalcEventArgs(expr, val, this);
CustomCalc(this, e);
val = e.CalculatedObject;
}
return val;
}
else if (cachedObject is Parameter)
{
return (cachedObject as Parameter).Value;
}
else if (cachedObject is Total)
{
object val = (cachedObject as Total).Value;
if (ConvertNulls && (val == null || val is DBNull))
val = 0;
(cachedObject as Total).ExecuteTotal(val);
return val;
}
// calculate the expression
return CalcExpression(expression, value);
}
private object ConvertToColumnDataType(object val, Type dataType, bool convertNulls)
{
if (val == null || val is DBNull)
{
if (convertNulls)
val = Converter.ConvertNull(dataType);
}
else
{
if (val is IConvertible)
{
Type t = Nullable.GetUnderlyingType(dataType);
try
{
val = Convert.ChangeType(val, t != null ? t : dataType);
}
catch (InvalidCastException)
{
// do nothing
}
catch (FormatException)
{
// do nothing
}
}
}
return val;
}
///
/// Returns an expression value.
///
/// The expression.
/// The value of currently printing object.
/// Returns the result of calculation.
///
/// This method is for internal use only, do not call it directly.
///
protected virtual object CalcExpression(string expression, Variant value)
{
if (expression.ToLower() == "true" || expression.ToLower() == "false")
{
expression = expression.ToLower();
}
// try to calculate the expression
foreach (AssemblyDescriptor d in assemblies)
{
if (d.ContainsExpression(expression))
return d.CalcExpression(expression, value);
}
#if REFLECTION_EMIT_COMPILER
if (Config.CompilerSettings.ReflectionEmitCompiler)
if (TryReflectionEmit(expression, value, out object returnValue))
return returnValue;
#endif
// expression not found. Probably it was added after the start of the report.
// Compile new assembly containing this expression.
using (AssemblyDescriptor descriptor = new AssemblyDescriptor(this, CodeHelper.EmptyScript()))
{
assemblies.Add(descriptor);
descriptor.AddObjects();
descriptor.AddSingleExpression(expression);
descriptor.AddFunctions();
descriptor.Compile();
return descriptor.CalcExpression(expression, value);
}
}
///
/// Invokes the script method with given name.
///
/// The name of the script method.
/// The method parameters.
public object InvokeMethod(string name, object[] parms)
{
if (assemblies.Count > 0)
return assemblies[0].InvokeMethod(name, parms);
return null;
}
private Column GetColumn(string complexName)
{
if (String.IsNullOrEmpty(complexName))
return null;
CachedDataItem cachedItem = cachedDataItems[complexName] as CachedDataItem;
if (cachedItem != null)
return cachedItem.column;
string[] names = complexName.Split('.');
cachedItem = cachedDataItems[names[0]] as CachedDataItem;
DataSourceBase data = cachedItem != null ? cachedItem.dataSource : GetDataSource(names[0]);
return DataHelper.GetColumn(Dictionary, data, names, true);
}
private object GetColumnValue(string complexName, bool convertNull)
{
Column column = GetColumn(complexName);
if (column == null)
return null;
return ConvertToColumnDataType(column.Value, column.DataType, convertNull);
}
private Variant GetTotalValue(string name, bool convertNull)
{
object value = Dictionary.Totals.GetValue(name);
if (convertNull && (value == null || value is DBNull))
value = 0;
return new Variant(value);
}
///
/// Gets the data column's value. Automatically converts null value to 0, false or ""
/// depending on the column type.
///
/// The name of the data column including the datasource name.
/// If report is running, returns the column value. Otherwise returns null.
///
/// The return value of this method does not depend on the property.
///
///
///
/// string employeeName = (string)report.GetColumnValue("Employees.FirstName");
///
///
public object GetColumnValue(string complexName)
{
return GetColumnValue(complexName, true);
}
///
/// Gets the data column's value. This method does not convert null values.
///
/// The name of the data column including the datasource name.
/// If report is running, returns the column value.
/// Otherwise returns null.
public object GetColumnValueNullable(string complexName)
{
return GetColumnValue(complexName, false);
}
///
/// Gets the report parameter with given name.
///
/// The name of the parameter.
/// The object if found, otherwise null.
///
/// To find nested parameter, use the "." separator: "MainParameter.NestedParameter"
///
public Parameter GetParameter(string complexName)
{
if (IsRunning)
return cachedDataItems[complexName] as Parameter;
return DataHelper.GetParameter(Dictionary, complexName);
}
///
/// Gets a value of the parameter with given name.
///
/// The name of the parameter.
/// The parameter's value if found, otherwise null.
///
/// To find nested parameter, use the "." separator: "MainParameter.NestedParameter"
///
public object GetParameterValue(string complexName)
{
Parameter par = GetParameter(complexName);
if (par != null)
{
// avoid InvalidCastException when casting object that is int to double
if (par.DataType.Name == "Double" && par.Value.GetType() == typeof(int))
{
return (double)(int)par.Value;
}
return par.Value;
}
return null;
}
///
/// Sets the parameter's value.
///
/// The name of the parameter.
/// Value to set.
///
/// Use this method to pass a value to the parameter that you've created in the "Data" window.
/// Such parameter may be used everythere in a report; for example, you can print its value
/// or use it in expressions.
/// You should call this method after the report was loaded and before you run it.
/// To access a nested parameter, use the "." separator: "MainParameter.NestedParameter"
///
/// This method will create the parameter if it does not exist.
///
///
/// This example shows how to pass a value to the parameter with "MyParam" name:
///
/// // load the report
/// report1.Load("report.frx");
/// // setup the parameter
/// report1.SetParameterValue("MyParam", 10);
/// // show the report
/// report1.Show();
///
///
public void SetParameterValue(string complexName, object value)
{
Parameter par = GetParameter(complexName);
if (par == null)
par = DataHelper.CreateParameter(Dictionary, complexName);
if (par != null)
{
par.Value = value;
par.Expression = "";
}
isParameterChanged = true;
}
///
/// Gets a value of the system variable with specified name.
///
/// Name of a variable.
/// The variable's value if found, otherwise null.
public object GetVariableValue(string complexName)
{
return GetParameterValue(complexName);
}
///
/// Gets a value of the total with specified name.
///
/// Name of total.
/// The total's value if found, otherwise 0.
/// This method converts null values to 0 if the property is set to true.
/// Use the method if you don't want the null conversion.
///
public Variant GetTotalValue(string name)
{
return GetTotalValue(name, ConvertNulls);
}
///
/// Gets a value of the total with specified name.
///
/// Name of total.
/// The total's value if found, otherwise null.
public Variant GetTotalValueNullable(string name)
{
return GetTotalValue(name, false);
}
///
/// Gets the datasource with specified name.
///
/// Alias name of a datasource.
/// The datasource object if found, otherwise null.
public DataSourceBase GetDataSource(string alias)
{
return Dictionary.FindByAlias(alias) as DataSourceBase;
}
#endregion Script related
#region Public Methods
///
public override void Assign(Base source)
{
BaseAssign(source);
}
///
/// Aborts the report execution.
///
public void Abort()
{
SetAborted(true);
}
///
public override Base FindObject(string name)
{
foreach (Base c in AllNamedObjects)
{
if (String.Compare(name, c.Name, true) == 0)
return c;
}
return null;
}
///
public override void Clear()
{
base.Clear();
ClearReportProperties();
}
///
/// Updates the report component's styles.
///
///
/// Call this method if you change the collection.
///
public void ApplyStyles()
{
foreach (Base c in AllObjects)
{
if (c is ReportComponentBase)
(c as ReportComponentBase).Style = (c as ReportComponentBase).Style;
}
}
///
/// Sets prepared pages.
///
///
public void SetPreparedPages(Preview.PreparedPages pages)
{
preparedPages = pages;
if (pages != null)
pages.SetReport(this);
}
internal void SetAborted(bool value)
{
aborted = value;
}
internal void SetOperation(ReportOperation operation)
{
this.operation = operation;
}
///
/// This method fires the StartReport event and the script code connected
/// to the StartReportEvent.
///
public void OnStartReport(EventArgs e)
{
SetRunning(true);
if (StartReport != null)
StartReport(this, e);
InvokeMethod(StartReportEvent, new object[] { this, e });
}
///
/// This method fires the FinishReport event and the script code connected
/// to the FinishReportEvent.
///
public void OnFinishReport(EventArgs e)
{
SetRunning(false);
if (FinishReport != null)
FinishReport(this, e);
InvokeMethod(FinishReportEvent, new object[] { this, e });
}
///
/// Runs the Export event.
///
/// ExportReportEventArgs object.
public void OnExportParameters(ExportParametersEventArgs e)
{
if (ExportParameters != null)
{
ExportParameters(this, e);
}
}
///
/// Add the name of the assembly (in addition to the default) that will be used to compile the report script
///
/// Assembly name
///
/// For example: report.AddReferencedAssembly("Newtonsoft.Json.dll")
///
public void AddReferencedAssembly(string assembly_name)
{
string[] assemblies = ReferencedAssemblies;
Array.Resize(ref assemblies, assemblies.Length + 1);
assemblies[assemblies.Length - 1] = assembly_name;
ReferencedAssemblies = assemblies;
}
///
/// Add the names of the assembly (in addition to the default) that will be used to compile the report script
///
/// Assembly's names
public void AddReferencedAssembly(IList assembly_names)
{
string[] assemblies = ReferencedAssemblies;
int oldLength = assemblies.Length;
Array.Resize(ref assemblies, oldLength + assembly_names.Count);
for (int i = 0; i < assembly_names.Count; i++)
{
assemblies[oldLength + i] = assembly_names[i];
}
ReferencedAssemblies = assemblies;
}
///
public override void Serialize(FRWriter writer)
{
Report c = writer.DiffObject as Report;
writer.ItemName = IsAncestor ? "inherited" : ClassName;
if (BaseReport != c.BaseReport)
{
// when save to the report file, convert absolute path to the base report to relative path
// (based on the main report path). Do not convert when saving to the clipboard.
string value = writer.SerializeTo != SerializeTo.Undo ? GetRelativePathToBaseReport() : BaseReport;
writer.WriteStr("BaseReport", value);
// Fix bug with moving child report to another folder without parent report.
if (writer.SerializeTo == SerializeTo.Report)
writer.WriteStr("BaseReportAbsolutePath", BaseReport);
}
// always serialize ScriptLanguage because its default value depends on Config.ReportSettings.DefaultLanguage
writer.WriteValue("ScriptLanguage", ScriptLanguage);
if (ScriptText != c.ScriptText)
writer.WritePropertyValue("ScriptText", ScriptText);
if (!writer.AreEqual(ReferencedAssemblies, c.ReferencedAssemblies))
writer.WriteValue("ReferencedAssemblies", ReferencedAssemblies);
if (ConvertNulls != c.ConvertNulls)
writer.WriteBool("ConvertNulls", ConvertNulls);
if (DoublePass != c.DoublePass)
writer.WriteBool("DoublePass", DoublePass);
if (Compressed != c.Compressed)
writer.WriteBool("Compressed", Compressed);
if (UseFileCache != c.UseFileCache)
writer.WriteBool("UseFileCache", UseFileCache);
if (TextQuality != c.TextQuality)
writer.WriteValue("TextQuality", TextQuality);
if (SmoothGraphics != c.SmoothGraphics)
writer.WriteBool("SmoothGraphics", SmoothGraphics);
if (Password != c.Password)
writer.WriteStr("Password", Password);
if (InitialPageNumber != c.InitialPageNumber)
writer.WriteInt("InitialPageNumber", InitialPageNumber);
if (MaxPages != c.MaxPages)
writer.WriteInt("MaxPages", MaxPages);
if (StartReportEvent != c.StartReportEvent)
writer.WriteStr("StartReportEvent", StartReportEvent);
if (FinishReportEvent != c.FinishReportEvent)
writer.WriteStr("FinishReportEvent", FinishReportEvent);
ReportInfo.Serialize(writer, c.ReportInfo);
SerializeDesign(writer, c);
if (Styles.Count > 0)
writer.Write(Styles);
writer.Write(Dictionary);
if (writer.SaveChildren)
{
foreach (Base child in ChildObjects)
{
writer.Write(child);
}
}
}
///
public override void Deserialize(FRReader reader)
{
if (reader.HasProperty("BaseReportAbsolutePath"))
{
BaseReportAbsolutePath = reader.ReadStr("BaseReportAbsolutePath");
}
base.Deserialize(reader);
// call OnAfterLoad method of each report object
foreach (Base c in AllObjects)
{
c.OnAfterLoad();
}
}
///
/// Saves the report to a stream.
///
/// The stream to save to.
public void Save(Stream stream)
{
using (FRWriter writer = new FRWriter())
{
if (IsAncestor)
writer.GetDiff += new DiffEventHandler(GetDiff);
writer.Write(this);
List disposeList = new List();
if (Compressed)
{
stream = Compressor.Compress(stream);
disposeList.Add(stream);
}
if (!String.IsNullOrEmpty(Password))
{
stream = Crypter.Encrypt(stream, Password);
disposeList.Insert(0, stream);
}
writer.Save(stream);
foreach (Stream s in disposeList)
{
s.Dispose();
}
}
}
///
/// Saves the report to a file.
///
/// The name of the file to save to.
public void Save(string fileName)
{
FileName = fileName;
using (FileStream f = new FileStream(fileName, FileMode.Create))
{
Save(f);
}
}
///
/// Saves the report to a stream with randomized values in data sources.
///
/// The stream to save to.
public void SaveWithRandomData(Stream stream)
{
FRRandom random = new FRRandom();
random.RandomizeDataSources(Dictionary.DataSources);
Save(stream);
}
///
/// Saves the report to a file with randomized values in data sources.
///
/// The name of the file to save to.
public void SaveWithRandomData(string fileName)
{
FRRandom random = new FRRandom();
random.RandomizeDataSources(Dictionary.DataSources);
Save(fileName);
}
///
/// Loads report from a stream.
///
/// The stream to load from.
///
/// The stream must be seekable.
/// When you load a password-protected report, you should specify a password in the property,
/// otherwise you will get the . In this case you should ask for a password and try again:
///
/// try
/// {
/// report.Load(stream);
/// }
/// catch (DecryptException)
/// {
/// report.Password = report.ShowPasswordForm(); // or use your own form to do this
/// report.Load(stream);
/// }
///
///
public void Load(Stream stream)
{
string password = Password;
Clear();
var saveStream = stream;
var saveStreamPos = stream.Position;
using (FRReader reader = new FRReader(this))
{
List disposeList = new List();
if (Compressor.IsStreamCompressed(stream))
{
stream = Compressor.Decompress(stream, true);
disposeList.Add(stream);
}
bool crypted = Crypter.IsStreamEncrypted(stream);
if (crypted)
{
stream = Crypter.Decrypt(stream, password);
disposeList.Add(stream);
}
try
{
reader.Load(stream);
}
catch (Exception e)
{
if (crypted)
{
saveStream.Position = saveStreamPos;
throw new DecryptException();
}
throw e;
}
finally
{
foreach (Stream s in disposeList)
{
try
{
s.Dispose();
}
catch
{
}
}
}
reader.Read(this);
}
}
///
/// Loads the report from a file.
///
/// The name of the file to load from.
///
/// When you try to load the password-protected report, you will be asked
/// for a password. You also may specify the password in the
/// property before loading the report. In this case the report will load silently.
///
public void Load(string fileName)
{
this.fileName = "";
using (FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
this.fileName = fileName;
Load(f);
}
}
///
/// Loads the report from a string.
///
/// The string that contains a stream in UTF8 or Base64 encoding.
public void LoadFromString(string s)
{
if (String.IsNullOrEmpty(s))
return;
byte[] buffer;
int startIndex = s.IndexOf("");
if (startIndex != -1)
{
buffer = Encoding.UTF8.GetBytes(s.Substring(startIndex));
}
else
{
buffer = Convert.FromBase64String(s);
}
using (MemoryStream stream = new MemoryStream(buffer))
{
Load(stream);
}
}
///
/// Saves the report to a string.
///
/// The string that contains a stream.
public string SaveToString()
{
using (MemoryStream stream = new MemoryStream())
{
Save(stream);
if (Compressed || !String.IsNullOrEmpty(Password))
{
return Convert.ToBase64String(stream.ToArray());
}
else
{
return Encoding.UTF8.GetString(stream.ToArray());
}
}
}
///
/// Saves the report to a string using the Base64 encoding.
///
/// The string that contains a stream.
public string SaveToStringBase64()
{
using (MemoryStream stream = new MemoryStream())
{
Save(stream);
return Convert.ToBase64String(stream.ToArray());
}
}
///
/// Creates the report instance and loads the report from a stream.
///
/// The stream to load from.
/// The new report instance.
public static Report FromStream(Stream stream)
{
Report result = new Report();
result.Load(stream);
return result;
}
///
/// Creates the report instance and loads the report from a file.
///
/// The name of the file to load from.
/// The new report instance.
public static Report FromFile(string fileName)
{
Report result = new Report();
result.Load(fileName);
return result;
}
///
/// Creates the report instance and loads the report from a string.
///
/// The string that contains a stream in UTF8 encoding.
/// The new report instance.
public static Report FromString(string utf8String)
{
Report result = new Report();
result.LoadFromString(utf8String);
return result;
}
///
/// Registers the application dataset with all its tables and relations to use it in the report.
///
/// The application data.
///
/// If you register more than one dataset, use the method.
///
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(dataSet1);
///
///
public void RegisterData(DataSet data)
{
Dictionary.RegisterDataSet(data, "Data", false);
}
///
/// Registers the application dataset with all its tables and relations to use it in the report and enables all its tables.
///
/// The application data.
/// The boolean value indicating whether all tables should be enabled.
///
/// If you register more than one dataset, use the method.
///
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(dataSet1, true);
///
///
public void RegisterData(DataSet data, bool enableAllTables)
{
Dictionary.RegisterDataSet(data, "Data", false);
foreach (DataTable table in data.Tables)
{
DataSourceBase ds = Report.GetDataSource(table.TableName);
if (ds != null)
ds.Enabled = true;
}
}
///
/// Registers the application dataset with specified name.
///
/// The application data.
/// The name of the data.
///
/// Use this method if you register more than one dataset. You may specify any value
/// for the name parameter: it is not displayed anywhere in the designer and used only
/// to load/save a report. The name must be persistent and unique for each registered dataset.
///
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(dataSet1, "NorthWind");
///
///
public void RegisterData(DataSet data, string name)
{
if (initializing)
{
initializeData = data;
initializeDataName = name;
}
else
Dictionary.RegisterDataSet(data, name, false);
}
///
/// Registers the application dataset with specified name and enables all its tables.
///
/// The application data.
/// The name of the data.
/// The boolean value indicating whether all tables should be enabled.
///
/// Use this method if you register more than one dataset. You may specify any value
/// for the name parameter: it is not displayed anywhere in the designer and used only
/// to load/save a report. The name must be persistent and unique for each registered dataset.
///
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(dataSet1, "NorthWind", true);
///
///
public void RegisterData(DataSet data, string name, bool enableAllTables)
{
if (initializing)
{
initializeData = data;
initializeDataName = name;
}
else
{
Dictionary.RegisterDataSet(data, name, false);
foreach (DataTable table in data.Tables)
{
DataSourceBase ds = Report.GetDataSource(table.TableName);
if (ds != null)
ds.Enabled = true;
}
}
}
///
/// Registers the application data table to use it in the report.
///
/// The application data.
/// The name of the data.
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(dataSet1.Tables["Orders"], "Orders");
///
///
public void RegisterData(DataTable data, string name)
{
Dictionary.RegisterDataTable(data, name, false);
}
///
/// Registers the application data view to use it in the report.
///
/// The application data.
/// The name of the data.
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(myDataView, "OrdersView");
///
///
public void RegisterData(DataView data, string name)
{
Dictionary.RegisterDataView(data, name, false);
}
///
/// Registers the application data relation to use it in the report.
///
/// The application data.
/// The name of the data.
///
/// You may specify any value for the name parameter: it is not displayed anywhere
/// in the designer and used only to load/save a report. The name must be persistent
/// and unique for each registered relation.
///
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(myDataRelation, "myRelation");
///
///
public void RegisterData(DataRelation data, string name)
{
Dictionary.RegisterDataRelation(data, name, false);
}
///
/// Obsolete. Registers the application business object to use it in the report.
///
/// Application data.
/// Name of the data.
/// Not used.
/// Maximum nesting level of business objects.
///
/// This method is obsolete. Use the method instead.
///
public void RegisterData(IEnumerable data, string name, BOConverterFlags flags, int maxNestingLevel)
{
RegisterData(data, name, maxNestingLevel);
}
///
/// Registers the application business object to use it in the report.
///
/// Application data.
/// Name of the data.
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(myBusinessObject, "Customers");
///
///
public void RegisterData(IEnumerable data, string name)
{
if (initializing)
{
initializeData = data;
initializeDataName = name;
}
else
Dictionary.RegisterBusinessObject(data, name, 1, false);
}
///
/// Registers the application business object to use it in the report.
///
/// Application data.
/// Name of the data.
/// Maximum nesting level of business objects.
///
/// This method creates initial datasource with specified nesting level. It is useful if
/// you create a report in code. In most cases, you don't need to specify the nesting level
/// because it may be selected in the designer's "Choose Report Data" dialog.
///
public void RegisterData(IEnumerable data, string name, int maxNestingLevel)
{
Dictionary.RegisterBusinessObject(data, name, maxNestingLevel, false);
}
///
/// Registers the application cube link to use it in the report.
///
/// The application data.
/// The name of the data.
///
///
/// report1.Load("report.frx");
/// report1.RegisterData(myCubeLink, "Orders");
///
///
public void RegisterData(IBaseCubeLink data, string name)
{
Dictionary.RegisterCubeLink(data, name, false);
}
///
/// Prepares the report.
///
/// true if report was prepared succesfully.
public bool Prepare()
{
return Prepare(false);
}
#if ASYNC
///
/// Prepares the report asynchronously.
///
/// Cancellation token
/// true if report was prepared succesfully.
[EditorBrowsable(EditorBrowsableState.Never)] // TODO
public Task PrepareAsync(CancellationToken token = default)
{
return PrepareAsync(false, token);
}
private async Task PrepareAsync(bool append, CancellationToken token = default)
{
SetRunning(true);
try
{
if (PreparedPages == null || !append)
{
ClearPreparedPages();
SetPreparedPages(new Preview.PreparedPages(this));
}
engine = new ReportEngine(this);
if (!Config.WebMode)
StartPerformanceCounter();
try
{
await CompileAsync(token).ConfigureAwait(false);
isParameterChanged = false;
return Engine.Run(true, append, true);
}
finally
{
if (!Config.WebMode)
StopPerformanceCounter();
}
}
finally
{
SetRunning(false);
}
}
#endif
///
/// Prepares the report.
///
/// Specifies whether the new report should be added to a
/// report that was prepared before.
/// true if report was prepared succesfully.
///
/// Use this method to merge prepared reports.
///
/// This example shows how to merge two reports and preview the result:
///
/// Report report = new Report();
/// report.Load("report1.frx");
/// report.Prepare();
/// report.Load("report2.frx");
/// report.Prepare(true);
/// report.ShowPrepared();
///
///
public bool Prepare(bool append)
{
SetRunning(true);
try
{
if (PreparedPages == null || !append)
{
ClearPreparedPages();
SetPreparedPages(new Preview.PreparedPages(this));
}
engine = new ReportEngine(this);
if (!Config.WebMode)
StartPerformanceCounter();
try
{
Compile();
isParameterChanged = false;
return Engine.Run(true, append, true);
}
finally
{
if (!Config.WebMode)
StopPerformanceCounter();
}
}
finally
{
SetRunning(false);
}
}
///
/// Prepares the report with pages limit.
///
/// Pages limit. The number of pages equal or less will be prepared.
/// true if report was prepared succesfully.
public bool Prepare(int pagesLimit)
{
SetRunning(true);
try
{
ClearPreparedPages();
SetPreparedPages(new Preview.PreparedPages(this));
engine = new ReportEngine(this);
if (!Config.WebMode)
StartPerformanceCounter();
try
{
Compile();
return Engine.Run(true, false, true, pagesLimit);
}
finally
{
if (!Config.WebMode)
StopPerformanceCounter();
}
}
finally
{
SetRunning(false);
}
}
///
/// For internal use only.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public void PreparePhase1()
{
bool webDialog = false;
SetRunning(true);
if (preparedPages != null)
{
// if prepared pages are set before => it's call method again => it's web dialog
webDialog = true;
preparedPages.Clear();
}
SetPreparedPages(new Preview.PreparedPages(this));
engine = new ReportEngine(this);
Compile();
Engine.RunPhase1(true, webDialog);
}
///
/// For internal use only.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public void PreparePhase2(int? pagesLimit = null)
{
Engine.RunPhase2(pagesLimit);
SetRunning(false);
}
///
/// Refresh the current report.
///
///
/// Call this method in the Click or MouseUp event handler of a report object to refresh
/// the currently previewed report. Report will be generated again, but without dialog forms.
///
public void Refresh()
{
needRefresh = true;
}
///
/// Refresh prepared report after interactive actions.
///
public void InteractiveRefresh()
{
PreparedPages.ClearPageCache();
InternalRefresh();
}
///
/// Serialize report object from string
///
/// Serialized report object from string
[EditorBrowsable(EditorBrowsableState.Never)]
public ReportComponentBase Xml(string xml)
{
XmlDocument doc = new XmlDocument();
using (TextReader reader = new StringReader(xml))
{
doc.WriteHeader = false;
doc.ReadHeader = true;
doc.Load(reader);
}
using (FRReader reader = new FRReader(this, doc.Root))
{
reader.DeserializeFrom = SerializeTo.Clipboard;
return reader.Read() as ReportComponentBase;
}
}
internal void InternalRefresh()
{
SetRunning(true);
try
{
Engine.Run(false, false, false);
}
finally
{
SetRunning(false);
}
}
internal TextRenderingHint GetTextQuality()
{
switch (this.TextQuality)
{
case TextQuality.Regular:
return TextRenderingHint.AntiAliasGridFit;
case TextQuality.ClearType:
return TextRenderingHint.ClearTypeGridFit;
case TextQuality.AntiAlias:
return TextRenderingHint.AntiAlias;
case TextQuality.SingleBPP:
return TextRenderingHint.SingleBitPerPixel;
case TextQuality.SingleBPPGridFit:
return TextRenderingHint.SingleBitPerPixelGridFit;
}
return TextRenderingHint.SystemDefault;
}
///
/// Prepare page
///
///
public void PreparePage(ReportPage page)
{
SetRunning(true);
try
{
Engine.Run(false, false, false, page);
}
finally
{
SetRunning(false);
}
}
///
/// Prepare page
///
///
/// Flag indicating whether the page is a detail page.
public void PreparePage(ReportPage page, bool isDetailPage)
{
bool pageVisible = page.Visible;
if (isDetailPage)
page.Visible = true;
PreparePage(page);
page.Visible = pageVisible;
}
///
/// Exports a report. Report should be prepared using the method.
///
/// The export filter.
/// Stream to save export result to.
public void Export(ExportBase export, Stream stream)
{
export.Export(this, stream);
}
///
/// Exports a report. Report should be prepared using the method.
///
/// The export filter.
/// File name to save export result to.
public void Export(ExportBase export, string fileName)
{
export.Export(this, fileName);
}
///
/// Saves the prepared report. Report should be prepared using the method.
///
/// File name to save to.
public void SavePrepared(string fileName)
{
if (PreparedPages != null)
PreparedPages.Save(fileName);
}
///
/// Saves the prepared report. Report should be prepared using the method.
///
/// Stream to save to.
public void SavePrepared(Stream stream)
{
if (PreparedPages != null)
PreparedPages.Save(stream);
}
///
/// Loads the prepared report from a .fpx file.
///
/// File name to load form.
public void LoadPrepared(string fileName)
{
isLoadPrepared = true;
if (PreparedPages == null)
SetPreparedPages(new FastReport.Preview.PreparedPages(this));
PreparedPages.Load(fileName);
}
///
/// Loads the prepared report from a .fpx file.
///
/// Stream to load from.
public void LoadPrepared(Stream stream)
{
isLoadPrepared = true;
if (PreparedPages == null)
SetPreparedPages(new FastReport.Preview.PreparedPages(this));
PreparedPages.Load(stream);
}
#endregion Public Methods
///
/// Initializes a new instance of the class with default settings.
///
public Report()
{
pages = new PageCollection(this);
reportInfo = new ReportInfo();
InitDesign();
styles = new StyleCollection();
Dictionary = new Dictionary();
graphicCache = new GraphicCache();
assemblies = new AssemblyCollection();
cachedDataItems = new Hashtable(StringComparer.InvariantCultureIgnoreCase); // needed for case insensitivity
storeInResources = true;
fileName = "";
autoFillDataSet = true;
tag = null;
ClearReportProperties();
SetFlags(Flags.CanMove | Flags.CanResize | Flags.CanDelete | Flags.CanEdit | Flags.CanChangeOrder |
Flags.CanChangeParent | Flags.CanCopy, false);
//FInlineImageCache = new InlineImageCache();
}
static Report()
{
Config.Init();
}
///
/// Ensure that static constructor is called.
///
public static void EnsureInit()
{
// do nothing, just ensure that static constructor is called.
}
///
/// Create name for all unnamed elements with prefix and start with number
///
/// Prefix for name
/// Number from which to start
public void PostNameProcess(string prefix, int number)
{
int i = number;
foreach (Base obj in AllObjects)
{
if (String.IsNullOrEmpty(obj.Name))
{
obj.SetName(prefix + i.ToString());
i++;
}
}
}
private class CachedDataItem
{
public DataSourceBase dataSource;
public Column column;
}
}
}