diff --git a/Extras/Core/FastReport.Data/Directory.Build.targets b/Extras/Core/FastReport.Data/Directory.Build.targets
index e76b4f8b..0c9b2eb8 100644
--- a/Extras/Core/FastReport.Data/Directory.Build.targets
+++ b/Extras/Core/FastReport.Data/Directory.Build.targets
@@ -2,10 +2,10 @@
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Cassandra/Shared.props b/Extras/Core/FastReport.Data/FastReport.Data.Cassandra/Shared.props
index 503b2404..953e4450 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.Cassandra/Shared.props
+++ b/Extras/Core/FastReport.Data/FastReport.Data.Cassandra/Shared.props
@@ -9,6 +9,7 @@
+
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/ClickHouseDataConnection.cs b/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/ClickHouseDataConnection.cs
index 687b40e4..69fa5b85 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/ClickHouseDataConnection.cs
+++ b/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/ClickHouseDataConnection.cs
@@ -10,6 +10,7 @@
using System.Data;
using System.Data.Common;
using System.Linq;
+using System.Net;
using System.Text;
using System.Threading.Tasks;
@@ -83,6 +84,7 @@ public override DbDataAdapter GetAdapter(string selectCommand, DbConnection conn
clickHouseDataAdapter.SelectCommand = command;
return clickHouseDataAdapter;
}
+
private string PrepareSelectCommand(string selectCommand, string tableName, DbConnection connection)
{
if (String.IsNullOrEmpty(selectCommand))
@@ -91,6 +93,7 @@ private string PrepareSelectCommand(string selectCommand, string tableName, DbCo
}
return selectCommand;
}
+
private IEnumerable GetColumns(ClickHouseDataReader reader)
{
for (int i = 0; i < reader.FieldCount; i++)
@@ -112,8 +115,8 @@ public override void FillTableSchema(DataTable table, string selectCommand, Comm
selectCommand = PrepareSelectCommand(selectCommand, table.TableName, clickHouseConnection);
/*To reduce size of traffic and size of answer from ClickHouse server.
Because FillSchema doesn't work in this ADO.NET library.
- LIMIT 0 gets an empy set, but we still have list of desired columns
- Prorably can be a better way.
+ LIMIT 0 gets an empty set, but we still have list of desired columns
+ Probably can be a better way.
*/
selectCommand += " LIMIT 0";
ClickHouseCommand clickHouseCommand = clickHouseConnection.CreateCommand();
@@ -121,7 +124,15 @@ Prorably can be a better way.
foreach (CommandParameter p in parameters)
{
selectCommand = selectCommand.Replace($"@{p.Name}", $"{{{p.Name}:{(ClickHouseTypeCode)p.DataType}}}");
- clickHouseCommand.AddParameter(p.Name, ((ClickHouseTypeCode)p.DataType).ToString(), p.Value);
+ if (p.Value is Variant value)
+ {
+ if (value.Type == typeof(string))
+ clickHouseCommand.AddParameter(p.Name, ((ClickHouseTypeCode)p.DataType).ToString(), VariantToClrType(value, (ClickHouseTypeCode)p.DataType));
+ else
+ clickHouseCommand.AddParameter(p.Name, ((ClickHouseTypeCode)p.DataType).ToString(), value.ToType(value.Type));
+ }
+ else
+ clickHouseCommand.AddParameter(p.Name, ((ClickHouseTypeCode)p.DataType).ToString(), p.Value);
}
clickHouseCommand.CommandText = selectCommand;
using (ClickHouseDataReader reader = clickHouseCommand.ExecuteReader() as ClickHouseDataReader)
@@ -135,5 +146,125 @@ Prorably can be a better way.
DisposeConnection(clickHouseConnection);
}
}
+
+ private object VariantToClrType(Variant value, ClickHouseTypeCode type)
+ {
+ if (value.ToString() == "" && type != ClickHouseTypeCode.Nothing)
+ return null;
+
+ switch (type)
+ {
+ case ClickHouseTypeCode.Enum8:
+ case ClickHouseTypeCode.Int8:
+ {
+ sbyte val = 0;
+ sbyte.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.Enum16:
+ case ClickHouseTypeCode.Int16:
+ {
+ short val = 0;
+ short.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.Int32:
+ {
+ int val = 0;
+ int.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.Int64:
+ {
+ long val = 0;
+ long.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.UInt8:
+ {
+ byte val = 0;
+ byte.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.UInt16:
+ {
+ ushort val = 0;
+ ushort.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.UInt32:
+ {
+ uint val = 0;
+ uint.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.UInt64:
+ {
+ ulong val = 0;
+ ulong.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.Date:
+ {
+ DateTime val = DateTime.Now;
+ DateTime.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.DateTime:
+ case ClickHouseTypeCode.DateTime64:
+ {
+ DateTimeOffset val = DateTimeOffset.Now;
+ DateTimeOffset.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.Decimal:
+ {
+ decimal val = 0;
+ decimal.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.Float32:
+ {
+ float val = 0;
+ float.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.Float64:
+ {
+ double val = 0;
+ double.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.UUID:
+ {
+ Guid val = Guid.Empty;
+ Guid.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case ClickHouseTypeCode.IPv6:
+ case ClickHouseTypeCode.IPv4:
+ {
+ try
+ {
+ return IPAddress.Parse(value.ToString());
+ }
+ catch
+ {
+ return IPAddress.None;
+ }
+ }
+ case ClickHouseTypeCode.Nothing:
+ return DBNull.Value;
+ case ClickHouseTypeCode.Array:
+ case ClickHouseTypeCode.Nested:
+ case ClickHouseTypeCode.Tuple:
+ case ClickHouseTypeCode.Nullable:
+ case ClickHouseTypeCode.LowCardinality:
+ throw new NotImplementedException();
+
+ default:
+ return value.ToString();
+ }
+ }
}
}
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/Shared.props b/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/Shared.props
index 531189a1..143262ee 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/Shared.props
+++ b/Extras/Core/FastReport.Data/FastReport.Data.ClickHouse/Shared.props
@@ -12,7 +12,7 @@
-
+
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Excel/Shared.props b/Extras/Core/FastReport.Data/FastReport.Data.Excel/Shared.props
index 36f2a6f9..62238dbd 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.Excel/Shared.props
+++ b/Extras/Core/FastReport.Data/FastReport.Data.Excel/Shared.props
@@ -10,6 +10,7 @@
+
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.MsSql/Shared.props b/Extras/Core/FastReport.Data/FastReport.Data.MsSql/Shared.props
index bf62f837..3c3523d6 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.MsSql/Shared.props
+++ b/Extras/Core/FastReport.Data/FastReport.Data.MsSql/Shared.props
@@ -8,7 +8,7 @@
-
+
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlDataConnection.cs b/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlDataConnection.cs
index 8b2e3016..181cfe4d 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlDataConnection.cs
+++ b/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlDataConnection.cs
@@ -3,6 +3,7 @@
using System.Data.Common;
using MySqlConnector;
using System.Data;
+using System.Globalization;
namespace FastReport.Data
{
@@ -64,7 +65,7 @@ public override DbDataAdapter GetAdapter(string selectCommand, DbConnection conn
{
MySqlDataAdapter adapter = new MySqlDataAdapter(selectCommand, connection as MySqlConnection);
foreach (CommandParameter p in parameters)
- {
+ {
MySqlParameter parameter = adapter.SelectCommand.Parameters.Add(p.Name, (MySqlDbType)p.DataType, p.Size);
if (p.Value is Variant value)
@@ -83,7 +84,7 @@ public override DbDataAdapter GetAdapter(string selectCommand, DbConnection conn
private object VariantToClrType(Variant value, MySqlDbType type)
{
- if (value.ToString() == "")
+ if (value.ToString() == "" && type != MySqlDbType.Null)
return null;
switch (type)
@@ -116,19 +117,16 @@ private object VariantToClrType(Variant value, MySqlDbType type)
return val;
}
case MySqlDbType.DateTime:
+ case MySqlDbType.Timestamp:
case MySqlDbType.Date:
+ case MySqlDbType.Newdate:
+ case MySqlDbType.Time:
{
DateTime val = DateTime.Now;
DateTime.TryParse(value.ToString(), out val);
return val;
}
- case MySqlDbType.Time:
- case MySqlDbType.Timestamp:
- {
- TimeSpan val = TimeSpan.Zero;
- TimeSpan.TryParse(value.ToString(), out val);
- return val;
- }
+ case MySqlDbType.NewDecimal:
case MySqlDbType.Decimal:
{
decimal val = 0;
@@ -142,13 +140,13 @@ private object VariantToClrType(Variant value, MySqlDbType type)
return val;
}
case MySqlDbType.Int24:
- case MySqlDbType.UInt24:
case MySqlDbType.Int32:
{
int val = 0;
int.TryParse(value.ToString(), out val);
return val;
}
+ case MySqlDbType.UInt24:
case MySqlDbType.UInt32:
{
uint val = 0;
@@ -174,12 +172,41 @@ private object VariantToClrType(Variant value, MySqlDbType type)
return val;
}
case MySqlDbType.Byte:
+ {
+ sbyte val = 0;
+ sbyte.TryParse(value.ToString(), out val);
+ return val;
+ }
case MySqlDbType.UByte:
{
byte val = 0;
byte.TryParse(value.ToString(), out val);
return val;
}
+ case MySqlDbType.Blob:
+ case MySqlDbType.TinyBlob:
+ case MySqlDbType.MediumBlob:
+ case MySqlDbType.LongBlob:
+ case MySqlDbType.Binary:
+ case MySqlDbType.VarBinary:
+ {
+ string val = value.ToString();
+ if (val.Length % 2 != 0)
+ {
+ throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", val));
+ }
+
+ byte[] data = new byte[val.Length / 2];
+ for (int index = 0; index < data.Length; index++)
+ {
+ string byteValue = val.Substring(index * 2, 2);
+ data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ }
+
+ return data;
+ }
+ case MySqlDbType.Null:
+ return DBNull.Value;
default:
return value.ToString();
}
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleDataConnection.cs b/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleDataConnection.cs
index 2fa974df..1b6a6041 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleDataConnection.cs
+++ b/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleDataConnection.cs
@@ -9,7 +9,7 @@ namespace FastReport.Data
{
public class OracleDataConnection : DataConnectionBase
{
- private void GetDBObjectNames(string name, string columnName, List list)
+ private void GetDBObjectNames(string name, string columnName, List list, bool ignoreShema = false)
{
DataTable schema = null;
DbConnection connection = GetConnection();
@@ -28,7 +28,7 @@ private void GetDBObjectNames(string name, string columnName, List list)
{
string tableName = row[columnName].ToString();
string schemaName = row["OWNER"].ToString();
- if (String.Compare(schemaName, "SYSTEM") == 0)
+ if (String.Compare(schemaName, "SYSTEM") == 0 || ignoreShema)
list.Add(tableName);
else
list.Add(schemaName + ".\"" + tableName + "\"");
@@ -57,7 +57,7 @@ protected override string GetConnectionStringWithLoginInfo(string userName, stri
builder.UserID = userName;
builder.Password = password;
-
+
return builder.ToString();
}
@@ -77,7 +77,6 @@ public override DbDataAdapter GetAdapter(string selectCommand, DbConnection conn
OracleDbType parType = (OracleDbType)p.DataType;
OracleParameter parameter = adapter.SelectCommand.Parameters.Add(p.Name, parType, p.Size);
parameter.Value = p.Value;
-
// if we have refcursor parameter, set its direction to output, and also
// modify the command type to CommandType.StoredProcedure. The selectCommand must contain
// the stored proc name only.
@@ -96,6 +95,177 @@ public override Type GetParameterType()
return typeof(OracleDbType);
}
+ public override string[] GetProcedureNames()
+ {
+ List list = new List();
+ GetDBObjectNames("Procedures", "OBJECT_NAME", list, true);
+ return list.ToArray();
+ }
+
+ private DataTable GetSchema(string selectCommand)
+ {
+ var connection = GetConnection();
+ try
+ {
+ OpenConnection(connection);
+
+ var dataset = new DataSet();
+ var adapter = new OracleDataAdapter(selectCommand, connection as OracleConnection);
+ adapter.Fill(dataset);
+
+ if (dataset.Tables.Count > 0)
+ return dataset.Tables[0];
+ }
+ finally
+ {
+ DisposeConnection(connection);
+ }
+
+ return null;
+ }
+
+ public override TableDataSource CreateProcedure(string tableName)
+ {
+ ProcedureDataSource table = new ProcedureDataSource();
+ table.Enabled = true;
+ table.SelectCommand = tableName;
+ DbConnection conn = GetConnection();
+ try
+ {
+ OpenConnection(conn);
+ var schemaParameters = GetSchema($"SELECT * FROM SYS.ALL_ARGUMENTS WHERE OBJECT_NAME='{tableName}'");
+ foreach (DataRow row in schemaParameters.Rows)
+ {
+ ParameterDirection direction = ParameterDirection.Input;
+ switch (row["IN_OUT"].ToString())
+ {
+ case "IN":
+ direction = ParameterDirection.Input;
+ table.Enabled = false;
+ break;
+ case "IN/OUT":
+ direction = ParameterDirection.InputOutput;
+ table.Enabled = false;
+ break;
+ case "OUT":
+ direction = ParameterDirection.Output;
+ break;
+ }
+;
+ if (!int.TryParse(row["DATA_PRECISION"].ToString(), out int precision))
+ precision = 0;
+ if (!int.TryParse(row["DATA_SCALE"].ToString(), out int scale))
+ scale = 0;
+ table.Parameters.Add(new ProcedureParameter()
+ {
+ Name = row["ARGUMENT_NAME"].ToString(),
+ DataType = (int)MapTypes(row["DATA_TYPE"].ToString(), Convert.ToInt32(precision), Convert.ToInt32(scale)),
+ Direction = direction,
+
+ });
+ }
+ }
+ finally
+ {
+ DisposeConnection(conn);
+ }
+
+ return table;
+ }
+
+ public OracleDbType MapTypes(string dataType, int precision, int scale)
+ {
+ switch (dataType)
+ {
+ case "BFILE":
+ return OracleDbType.BFile;
+ case "BINARY_FLOAT":
+ return OracleDbType.BinaryFloat;
+ case "BINARY_DOUBLE":
+ return OracleDbType.BinaryDouble;
+ case "BLOB":
+ return OracleDbType.Blob;
+ case "BOOLEAN":
+ case "PL/SQL BOOLEAN":
+ return OracleDbType.Boolean;
+ case "byte":
+ return OracleDbType.Byte;
+ case "CHAR":
+ return OracleDbType.Char;
+ case "CLOB":
+ return OracleDbType.Clob;
+ case "DATE":
+ return OracleDbType.Date;
+ case "NUMBER":
+ case "INTEGER":
+ case "FLOAT":
+ return ConvertNumberToOraDbType(precision, scale);
+ case "INTERVAL DAY TO SECOND":
+ return OracleDbType.IntervalDS;
+ case "INTERVAL YEAR TO MONTH":
+ return OracleDbType.IntervalYM;
+ case "LONG":
+ return OracleDbType.Long;
+ case "LONG RAW":
+ return OracleDbType.BFile;
+ case "NCHAR":
+ return OracleDbType.NChar;
+ case "NCLOB":
+ return OracleDbType.NClob;
+ case "NVARCHAR2":
+ return OracleDbType.NVarchar2;
+ case "RAW":
+ return OracleDbType.Raw;
+ case "REF CURSOR":
+ return OracleDbType.RefCursor;
+ case "TIMESTAMP":
+ return OracleDbType.TimeStamp;
+ case "TIMESTAMP WITH LOCAL TIME ZONE":
+ return OracleDbType.TimeStampLTZ;
+ case "TIMESTAMP WITH TIME ZONE":
+ return OracleDbType.TimeStampTZ;
+ case "VARCHAR2":
+ return OracleDbType.Varchar2;
+ case "OPAQUE/XMLTYPE":
+ case "XMLTYPE":
+ return OracleDbType.XmlType;
+ }
+
+ return OracleDbType.Varchar2;
+ }
+
+ private static OracleDbType ConvertNumberToOraDbType(int precision, int scale)
+ {
+ OracleDbType result = OracleDbType.Decimal;
+ if (scale <= 0 && precision - scale < 5)
+ {
+ result = OracleDbType.Int16;
+ }
+ else if (scale <= 0 && precision - scale < 10)
+ {
+ result = OracleDbType.Int32;
+ }
+ else if (scale <= 0 && precision - scale < 19)
+ {
+ result = OracleDbType.Int64;
+ }
+ else if (precision < 8 && ((scale <= 0 && precision - scale <= 38) || (scale > 0 && scale <= 44)))
+ {
+ result = OracleDbType.Single;
+ }
+ else if (precision < 16)
+ {
+ result = OracleDbType.Double;
+ }
+
+ return result;
+ }
+
+ public OracleDataConnection()
+ {
+ CanContainProcedures = true;
+ }
+
#if !FRCORE
public override int GetDefaultParameterType()
{
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/Shared.props b/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/Shared.props
index 3bc453cd..0397c100 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/Shared.props
+++ b/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/Shared.props
@@ -8,7 +8,7 @@
-
+
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresDataConnection.cs b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresDataConnection.cs
index ff351a04..4cf9b961 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresDataConnection.cs
+++ b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresDataConnection.cs
@@ -1,9 +1,12 @@
using Npgsql;
using NpgsqlTypes;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
+using System.Globalization;
+using System.Net.NetworkInformation;
namespace FastReport.Data
{
@@ -59,6 +62,133 @@ private DataTable GetSchema(string selectCommand)
return null;
}
+ private int MapTypes(string data_type)
+ {
+ var parenIndex = data_type.IndexOf('(');
+ if (parenIndex > -1)
+ data_type = data_type.Substring(0, parenIndex);
+
+ NpgsqlDbType result = NpgsqlDbType.Unknown;
+
+ switch (data_type)
+ {
+ // Numeric types
+ case "smallint": result = NpgsqlDbType.Smallint; break;
+ case "integer":
+ case "int": result = NpgsqlDbType.Integer; break;
+ case "bigint": result = NpgsqlDbType.Bigint; break;
+ case "real": result = NpgsqlDbType.Real; break;
+ case "double precision": result = NpgsqlDbType.Double; break;
+ case "numeric": result = NpgsqlDbType.Numeric; break;
+ case "money": result = NpgsqlDbType.Money; break;
+
+ // Text types
+ case "text": result = NpgsqlDbType.Text; break;
+ case "xml": result = NpgsqlDbType.Xml; break;
+ case "character varying":
+ case "varchar": result = NpgsqlDbType.Varchar; break;
+ case "character": result = NpgsqlDbType.Char; break;
+ case "name": result = NpgsqlDbType.Name; break;
+ case "refcursor": result = NpgsqlDbType.Refcursor; break;
+ case "citext": result = NpgsqlDbType.Citext; break;
+ case "jsonb": result = NpgsqlDbType.Jsonb; break;
+ case "json": result = NpgsqlDbType.Json; break;
+ case "jsonpath": result = NpgsqlDbType.JsonPath; break;
+
+ // Date/time types
+ case "timestamp without time zone":
+ case "timestamp": result = NpgsqlDbType.Timestamp; break;
+ case "timestamp with time zone":
+ case "timestamptz": result = NpgsqlDbType.TimestampTz; break;
+ case "date": result = NpgsqlDbType.Date; break;
+ case "time without time zone":
+ case "timetz": result = NpgsqlDbType.Time; break;
+ case "time with time zone":
+ case "time": result = NpgsqlDbType.TimeTz; break;
+ case "interval": result = NpgsqlDbType.Interval; break;
+
+ // Network types
+ case "cidr": result = NpgsqlDbType.Cidr; break;
+ case "inet": result = NpgsqlDbType.Inet; break;
+ case "macaddr": result = NpgsqlDbType.MacAddr; break;
+ case "macaddr8": result = NpgsqlDbType.MacAddr8; break;
+
+ // Full-text search types
+ case "tsquery": result = NpgsqlDbType.TsQuery; break;
+ case "tsvector": result = NpgsqlDbType.TsVector; break;
+
+ // Geometry types
+ case "box": result = NpgsqlDbType.Box; break;
+ case "circle": result = NpgsqlDbType.Circle; break;
+ case "line": result = NpgsqlDbType.Line; break;
+ case "lseg": result = NpgsqlDbType.LSeg; break;
+ case "path": result = NpgsqlDbType.Path; break;
+ case "point": result = NpgsqlDbType.Point; break;
+ case "polygon": result = NpgsqlDbType.Polygon; break;
+
+ // LTree types
+ case "lquery": result = NpgsqlDbType.LQuery; break;
+ case "ltree": result = NpgsqlDbType.LTree; break;
+ case "ltxtquery": result = NpgsqlDbType.LTxtQuery; break;
+
+ // UInt types
+ case "oid": result = NpgsqlDbType.Oid; break;
+ case "xid": result = NpgsqlDbType.Xid; break;
+ case "xid8": result = NpgsqlDbType.Xid8; break;
+ case "cid": result = NpgsqlDbType.Cid; break;
+ case "regtype": result = NpgsqlDbType.Regtype; break;
+ case "regconfig": result = NpgsqlDbType.Regconfig; break;
+
+ // Misc types
+ case "boolean":
+ case "bool": result = NpgsqlDbType.Boolean; break;
+ case "bytea": result = NpgsqlDbType.Bytea; break;
+ case "uuid": result = NpgsqlDbType.Uuid; break;
+ case "bit varying":
+ case "varbit": result = NpgsqlDbType.Varbit; break;
+ case "bit": result = NpgsqlDbType.Bit; break;
+ case "hstore": result = NpgsqlDbType.Hstore; break;
+
+ case "geometry": result = NpgsqlDbType.Geometry; break;
+ case "geography": result = NpgsqlDbType.Geography; break;
+
+ // Built-in range types
+ case "int4range": result = NpgsqlDbType.IntegerRange; break;
+ case "int8range": result = NpgsqlDbType.BigIntRange; break;
+ case "numrange": result = NpgsqlDbType.NumericRange; break;
+ case "tsrange": result = NpgsqlDbType.TimestampRange; break;
+ case "tstzrange": result = NpgsqlDbType.TimestampTzRange; break;
+ case "daterange": result = NpgsqlDbType.DateRange; break;
+
+ // Built-in multirange types
+ case "int4multirange": result = NpgsqlDbType.IntegerMultirange; break;
+ case "int8multirange": result = NpgsqlDbType.BigIntMultirange; break;
+ case "nummultirange": result = NpgsqlDbType.NumericMultirange; break;
+ case "tsmultirange": result = NpgsqlDbType.TimestampMultirange; break;
+ case "tstzmultirange": result = NpgsqlDbType.TimestampTzMultirange; break;
+ case "datemultirange": result = NpgsqlDbType.DateMultirange; break;
+
+ // Internal types
+ case "int2vector": result = NpgsqlDbType.Int2Vector; break;
+ case "oidvector": result = NpgsqlDbType.Oidvector; break;
+ case "pg_lsn": result = NpgsqlDbType.PgLsn; break;
+ case "tid": result = NpgsqlDbType.Tid; break;
+ case "char": result = NpgsqlDbType.InternalChar; break;
+
+ default:
+ {
+ if (data_type.EndsWith("[]", StringComparison.Ordinal))
+ {
+ NpgsqlDbType elementNpgsqlDbType = (NpgsqlDbType)MapTypes(data_type.Substring(0, data_type.Length - 2));
+ result = elementNpgsqlDbType != NpgsqlDbType.Unknown ? elementNpgsqlDbType | NpgsqlDbType.Array : NpgsqlDbType.Unknown; // e.g. ranges
+ }
+ break;
+ }
+ }
+
+ return (int)result;
+ }
+
///
public override string[] GetTableNames()
{
@@ -107,6 +237,7 @@ public override string[] GetProcedureNames()
"WHERE routine_type = 'FUNCTION'";
var schema = GetSchema(selectCommand);
+
if (schema != null)
{
foreach (DataRow row in schema.Rows)
@@ -183,13 +314,14 @@ public override TableDataSource CreateProcedure(string tableName)
case "OUT":
// skip completely: it's a result table's column
+ default:
continue;
}
table.Parameters.Add(new ProcedureParameter()
{
Name = row["parameter_name"].ToString(),
- DataType = (int)(NpgsqlDbType)Enum.Parse(typeof(NpgsqlDbType), row["data_type"].ToString(), true),
+ DataType = MapTypes(row["data_type"].ToString()),
Direction = direction
});
}
@@ -235,14 +367,289 @@ public override DbDataAdapter GetAdapter(string selectCommand, DbConnection conn
foreach (CommandParameter p in parameters)
{
NpgsqlParameter parameter = adapter.SelectCommand.Parameters.Add(p.Name, (NpgsqlDbType)p.DataType, p.Size);
- parameter.Value = p.Value;
+ if (p.Value is Variant value)
+ {
+ if (value.Type == typeof(string))
+ parameter.Value = VariantToClrType(value, (NpgsqlDbType)p.DataType);
+ else
+ parameter.Value = value.ToType(value.Type);
+ }
+ else
+ parameter.Value = p.Value;
}
return adapter;
}
+ private object VariantToClrType(Variant value, NpgsqlDbType type)
+ {
+ if (value.ToString() == "")
+ return null;
+
+ switch (type)
+ {
+ case NpgsqlDbType.Bigint:
+ {
+ long val = 0;
+ long.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Boolean:
+ {
+ bool val = false;
+ bool.TryParse(value.ToString(), out val);
+ if (value.ToString() == "1")
+ val = true;
+ return val;
+ }
+ case NpgsqlDbType.Double:
+ {
+ double val = 0;
+ double.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Timestamp:
+ case NpgsqlDbType.TimestampTz:
+ case NpgsqlDbType.Date:
+ {
+ DateTime val = DateTime.Now;
+ DateTime.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Time:
+ case NpgsqlDbType.Interval:
+ {
+ TimeSpan val = TimeSpan.Zero;
+ TimeSpan.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.TimeTz:
+ {
+ DateTimeOffset val = DateTimeOffset.Now;
+ DateTimeOffset.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Money:
+ case NpgsqlDbType.Numeric:
+ {
+ decimal val = 0;
+ decimal.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Real:
+ {
+ float val = 0;
+ float.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Integer:
+ {
+ int val = 0;
+ int.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Oid:
+ case NpgsqlDbType.Xid:
+ case NpgsqlDbType.Cid:
+ {
+ uint val = 0;
+ uint.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Tid:
+ {
+ string[] values = value.ToString().Replace("(", "").Replace(")", "").Split(',');
+
+ uint val1 = 0;
+ uint.TryParse(values[0], out val1);
+
+ ushort val2 = 0;
+ ushort.TryParse(values[1], out val2);
+
+ return new NpgsqlTid(val1, val2);
+ }
+ case NpgsqlDbType.Uuid:
+ {
+ Guid val = Guid.Empty;
+ Guid.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Smallint:
+ {
+ short val = 0;
+ short.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.InternalChar:
+ {
+ byte val = 0;
+ byte.TryParse(value.ToString(), out val);
+ return val;
+ }
+ case NpgsqlDbType.Box:
+ {
+ try
+ {
+ return PostgresTypesParsers.ParseBox(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlBox();
+ }
+ }
+ case NpgsqlDbType.Circle:
+ {
+ try
+ {
+ return PostgresTypesParsers.ParseCircle(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlCircle();
+ }
+ }
+ case NpgsqlDbType.Line:
+ {
+ try
+ {
+ return PostgresTypesParsers.ParseLine(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlLine();
+ }
+ }
+ case NpgsqlDbType.Polygon:
+ {
+ try
+ {
+ return PostgresTypesParsers.ParsePolygon(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlPolygon();
+ }
+ }
+ case NpgsqlDbType.Path:
+ {
+ try
+ {
+ return PostgresTypesParsers.ParsePath(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlPath();
+ }
+ }
+ case NpgsqlDbType.LSeg:
+ {
+ try
+ {
+ return PostgresTypesParsers.ParseLSeg(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlLSeg();
+ }
+ }
+ case NpgsqlDbType.Point:
+ {
+ try
+ {
+ return PostgresTypesParsers.ParsePoint(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlPoint();
+ }
+ }
+ case NpgsqlDbType.Cidr:
+ {
+ try
+ {
+ return new NpgsqlCidr(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlCidr();
+ }
+ }
+ case NpgsqlDbType.Inet:
+ {
+ try
+ {
+ return new NpgsqlInet(value.ToString());
+ }
+ catch
+ {
+ return new NpgsqlInet();
+ }
+ }
+ case NpgsqlDbType.MacAddr:
+ {
+ try
+ {
+ return PhysicalAddress.Parse(value.ToString());
+ }
+ catch
+ {
+ return PhysicalAddress.None;
+ }
+ }
+ case NpgsqlDbType.TsQuery:
+ {
+ try
+ {
+ return NpgsqlTsQuery.Parse(value.ToString());
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ case NpgsqlDbType.TsVector:
+ {
+ try
+ {
+ return NpgsqlTsVector.Parse(value.ToString());
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ case NpgsqlDbType.Bytea:
+ {
+ string val = value.ToString();
+ if (val.Length % 2 != 0)
+ {
+ throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", val));
+ }
+
+ byte[] data = new byte[val.Length / 2];
+ for (int index = 0; index < data.Length; index++)
+ {
+ string byteValue = val.Substring(index * 2, 2);
+ data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ }
+
+ return data;
+ }
+ case NpgsqlDbType.Array:
+ case NpgsqlDbType.Range:
+ case NpgsqlDbType.Hstore:
+ case NpgsqlDbType.Oidvector:
+ case NpgsqlDbType.MacAddr8:
+ case NpgsqlDbType.Int2Vector:
+ throw new NotImplementedException();
+ default:
+ return value.ToString();
+ }
+ }
+
public PostgresDataConnection()
{
CanContainProcedures = true;
+ AppContext.SetSwitch("Npgsql.EnableStoredProcedureCompatMode", true);
}
}
}
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresTypesParsers.cs b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresTypesParsers.cs
new file mode 100644
index 00000000..248bf182
--- /dev/null
+++ b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresTypesParsers.cs
@@ -0,0 +1,117 @@
+using NpgsqlTypes;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+
+namespace FastReport.Data
+{
+ internal static class PostgresTypesParsers
+ {
+ static readonly Regex pointRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)", RegexOptions.Compiled);
+ static readonly Regex lineRegex = new Regex(@"\{(-?\d+.?\d*),(-?\d+.?\d*),(-?\d+.?\d*)\}", RegexOptions.Compiled);
+ static readonly Regex lSegRegex = new Regex(@"\[\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)\]", RegexOptions.Compiled);
+ static readonly Regex boxRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)", RegexOptions.Compiled);
+ static readonly Regex circleRegex = new Regex(@"<\((-?\d+.?\d*),(-?\d+.?\d*)\),(\d+.?\d*)>", RegexOptions.Compiled);
+
+ public static NpgsqlPoint ParsePoint(string s)
+ {
+ var m = pointRegex.Match(s);
+ if (!m.Success)
+ {
+ throw new FormatException("Not a valid point: " + s);
+ }
+ return new NpgsqlPoint(double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat));
+ }
+
+ public static NpgsqlLine ParseLine(string s)
+ {
+ var m = lineRegex.Match(s);
+ if (!m.Success)
+ throw new FormatException("Not a valid line: " + s);
+ return new NpgsqlLine(
+ double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)
+ );
+ }
+
+ public static NpgsqlLSeg ParseLSeg(string s)
+ {
+ var m = lSegRegex.Match(s);
+ if (!m.Success)
+ {
+ throw new FormatException("Not a valid line: " + s);
+ }
+ return new NpgsqlLSeg(
+ double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[4].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)
+ );
+
+ }
+
+ public static NpgsqlBox ParseBox(string s)
+ {
+ var m = boxRegex.Match(s);
+ return new NpgsqlBox(
+ new NpgsqlPoint(double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)),
+ new NpgsqlPoint(double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[4].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat))
+ );
+ }
+
+ public static NpgsqlPath ParsePath(string s)
+ {
+ if (s[0] != '[' && s[0] != '(')
+ throw new Exception("Invalid path string: " + s);
+
+ var open = s[0] == '[';
+
+ var result = new NpgsqlPath(open);
+ var i = 1;
+ while (true)
+ {
+ var i2 = s.IndexOf(')', i);
+ result.Add(ParsePoint(s.Substring(i, i2 - i + 1)));
+ if (s[i2 + 1] != ',')
+ break;
+ i = i2 + 2;
+ }
+ return result;
+ }
+
+ public static NpgsqlPolygon ParsePolygon(string s)
+ {
+ var points = new List();
+ var i = 1;
+ while (true)
+ {
+ var i2 = s.IndexOf(')', i);
+ points.Add(ParsePoint(s.Substring(i, i2 - i + 1)));
+ if (s[i2 + 1] != ',')
+ break;
+ i = i2 + 2;
+ }
+ return new NpgsqlPolygon(points);
+ }
+
+ public static NpgsqlCircle ParseCircle(string s)
+ {
+ var m = circleRegex.Match(s);
+ if (!m.Success)
+ throw new FormatException("Not a valid circle: " + s);
+
+ return new NpgsqlCircle(
+ double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
+ double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)
+ );
+ }
+
+ }
+}
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/Shared.props b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/Shared.props
index 32fb5d95..d90813f8 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/Shared.props
+++ b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/Shared.props
@@ -8,7 +8,7 @@
-
+
diff --git a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/Shared.props b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/Shared.props
index 290ff1d1..e678cb14 100644
--- a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/Shared.props
+++ b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/Shared.props
@@ -9,6 +9,9 @@
+
+
+
diff --git a/Extras/Core/FastReport.Plugin/Directory.Build.targets b/Extras/Core/FastReport.Plugin/Directory.Build.targets
index e76b4f8b..757c5e90 100644
--- a/Extras/Core/FastReport.Plugin/Directory.Build.targets
+++ b/Extras/Core/FastReport.Plugin/Directory.Build.targets
@@ -3,9 +3,9 @@
-
+
-
+
\ No newline at end of file
diff --git a/FastReport.Base/BandBase.Async.cs b/FastReport.Base/BandBase.Async.cs
new file mode 100644
index 00000000..da0bdb29
--- /dev/null
+++ b/FastReport.Base/BandBase.Async.cs
@@ -0,0 +1,28 @@
+using FastReport.Utils;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport
+{
+ public abstract partial class BandBase
+ {
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+
+ FRCollectionBase list = new FRCollectionBase();
+ Objects.CopyTo(list);
+ foreach (ReportComponentBase obj in list)
+ {
+ await obj.GetDataAsync(cancellationToken);
+ obj.OnAfterData();
+
+ // break the component if it is of BreakableComponent an has non-empty BreakTo property
+ if (obj is BreakableComponent && (obj as BreakableComponent).BreakTo != null &&
+ (obj as BreakableComponent).BreakTo.GetType() == obj.GetType())
+ (obj as BreakableComponent).Break((obj as BreakableComponent).BreakTo);
+ }
+ OnAfterData();
+ }
+ }
+}
diff --git a/FastReport.Base/BandBase.cs b/FastReport.Base/BandBase.cs
index c2f4bd43..2fc1d12a 100644
--- a/FastReport.Base/BandBase.cs
+++ b/FastReport.Base/BandBase.cs
@@ -744,9 +744,6 @@ public override float CalcHeight()
if (obj.GrowToBottom)
{
obj.Height = Height - obj.Top;
- // reserve place for border
- if ((!(Parent is GroupHeaderBand parent) || (parent.isLastRow && parent.DataBand == this)) && Child == null && IsLastRow && obj.Border.Lines.HasFlag(BorderLines.Bottom))
- obj.Height -= Border.BottomLine.Width;
}
}
diff --git a/FastReport.Base/Barcode/BarcodeObject.Async.cs b/FastReport.Base/Barcode/BarcodeObject.Async.cs
new file mode 100644
index 00000000..5e5e0d00
--- /dev/null
+++ b/FastReport.Base/Barcode/BarcodeObject.Async.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport.Barcode
+{
+ public partial class BarcodeObject
+ {
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+ #endregion
+ }
+}
diff --git a/FastReport.Base/Barcode/BarcodeObject.cs b/FastReport.Base/Barcode/BarcodeObject.cs
index 0200f03a..1c49f794 100644
--- a/FastReport.Base/Barcode/BarcodeObject.cs
+++ b/FastReport.Base/Barcode/BarcodeObject.cs
@@ -593,6 +593,11 @@ public override void RestoreState()
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
if (!String.IsNullOrEmpty(DataColumn))
{
object value = Report.GetColumnValue(DataColumn);
diff --git a/FastReport.Base/Base.cs b/FastReport.Base/Base.cs
index f362fd84..62937904 100644
--- a/FastReport.Base/Base.cs
+++ b/FastReport.Base/Base.cs
@@ -993,6 +993,28 @@ internal void AssignAll(Base source, bool assignName)
}
}
+ internal void AssignAll(Base source, bool assignName, bool assignAncestor)
+ {
+ Clear();
+ Assign(source);
+ if (assignName)
+ SetName(source.Name);
+
+ if (assignAncestor)
+ SetAncestor(source.IsAncestor);
+
+ foreach (Base child in source.ChildObjects)
+ {
+ Base myChild = Activator.CreateInstance(child.GetType()) as Base;
+ myChild.SetReport(Report);
+ myChild.AssignAll(child, assignName, assignAncestor);
+ myChild.SetReport(null);
+ myChild.Parent = this;
+ if (assignAncestor)
+ myChild.SetAncestor(child.IsAncestor);
+ }
+ }
+
///
/// Gets a value indicating whether the object has the specified parent in its parent hierarchy.
///
diff --git a/FastReport.Base/CheckBoxObject.Async.cs b/FastReport.Base/CheckBoxObject.Async.cs
new file mode 100644
index 00000000..65947e78
--- /dev/null
+++ b/FastReport.Base/CheckBoxObject.Async.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport
+{
+ public partial class CheckBoxObject
+ {
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+ #endregion
+
+ }
+}
diff --git a/FastReport.Base/CheckBoxObject.cs b/FastReport.Base/CheckBoxObject.cs
index f6df5db8..ae420540 100644
--- a/FastReport.Base/CheckBoxObject.cs
+++ b/FastReport.Base/CheckBoxObject.cs
@@ -340,6 +340,11 @@ public override string[] GetExpressions()
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
if (!String.IsNullOrEmpty(DataColumn))
{
object value = Report.GetColumnValue(DataColumn);
diff --git a/FastReport.Base/Code/AssemblyDescriptor.Async.cs b/FastReport.Base/Code/AssemblyDescriptor.Async.cs
new file mode 100644
index 00000000..df0e99b8
--- /dev/null
+++ b/FastReport.Base/Code/AssemblyDescriptor.Async.cs
@@ -0,0 +1,207 @@
+using System;
+#if NETSTANDARD || NETCOREAPP
+using FastReport.Code.CodeDom.Compiler;
+#else
+using System.CodeDom.Compiler;
+#endif
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using FastReport.Utils;
+
+
+namespace FastReport.Code
+{
+ partial class AssemblyDescriptor : IDisposable
+ {
+ private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
+
+
+ private static async Task AddFastReportAssemblies(StringCollection assemblies, CancellationToken token)
+ {
+ foreach (Assembly assembly in RegisteredObjects.Assemblies)
+ {
+ string aLocation = assembly.Location;
+#if CROSSPLATFORM || COREWIN
+ if (aLocation == "")
+ {
+ // try fix SFA in FastReport.Compat
+ string fixedReference = await CodeDomProvider.TryFixReferenceInSingeFileAppAsync(assembly, token);
+ if (!string.IsNullOrEmpty(fixedReference))
+ aLocation = fixedReference;
+ }
+#endif
+ if (!ContainsAssembly(assemblies, aLocation))
+ assemblies.Add(aLocation);
+ }
+ }
+
+ public async Task CompileAsync(CancellationToken token = default)
+ {
+ if (needCompile)
+ {
+ await semaphoreSlim.WaitAsync(token);
+
+ if (needCompile)
+ {
+ try
+ {
+ await InternalCompileAsync(token);
+ }
+ finally
+ {
+ semaphoreSlim.Release();
+ }
+ }
+ }
+ }
+
+
+ private async Task InternalCompileAsync(CancellationToken cancellationToken)
+ {
+ CompilerParameters cp = await GetCompilerParametersAsync(cancellationToken);
+
+ if (Config.WebMode &&
+ Config.EnableScriptSecurity &&
+ Config.ScriptSecurityProps.AddStubClasses)
+ AddStubClasses();
+
+ string errors = string.Empty;
+ CompilerResults cr = await InternalCompileAsync(cp, cancellationToken);
+ bool success = CheckCompileResult(cr);
+ for (int i = 0; !success && i < Config.CompilerSettings.RecompileCount; i++)
+ {
+ cr = await TryRecompileAsync(cp, cr, cancellationToken);
+ success = CheckCompileResult(cr);
+ }
+
+ if (cr != null)
+ HandleCompileErrors(cr, out errors);
+
+ if (!success && errors != string.Empty)
+ throw new CompilerException(errors);
+ }
+
+ private static bool CheckCompileResult(CompilerResults result)
+ {
+ // if result == null => was found in cache
+ return result == null || result.Errors.Count == 0;
+ }
+
+
+ private async Task GetCompilerParametersAsync(CancellationToken ct)
+ {
+ // configure compiler options
+ CompilerParameters cp = new CompilerParameters();
+ await AddFastReportAssemblies(cp.ReferencedAssemblies, ct); // 2
+ AddReferencedAssemblies(cp.ReferencedAssemblies, currentFolder); // 9
+ ReviewReferencedAssemblies(cp.ReferencedAssemblies);
+ cp.GenerateInMemory = true;
+ // sometimes the system temp folder is not accessible...
+ if (Config.TempFolder != null)
+ cp.TempFiles = new TempFileCollection(Config.TempFolder, false);
+ return cp;
+ }
+
+ ///
+ /// Returns true, if compilation is successful
+ ///
+ private async Task InternalCompileAsync(CompilerParameters cp, CancellationToken cancellationToken)
+ {
+ CompilerResults cr;
+ // find assembly in cache
+ string assemblyHash = GetAssemblyHash(cp);
+ Assembly cachedAssembly;
+ if (FAssemblyCache.TryGetValue(assemblyHash, out cachedAssembly))
+ {
+ Assembly = cachedAssembly;
+ var reportScript = Assembly.CreateInstance("FastReport.ReportScript");
+ InitInstance(reportScript);
+ cr = null;
+ return cr; // return true;
+ }
+
+ // compile report scripts
+ using (CodeDomProvider provider = Report.CodeHelper.GetCodeProvider())
+ {
+ string script = scriptText.ToString();
+ ScriptSecurityEventArgs ssea = new ScriptSecurityEventArgs(Report, script, Report.ReferencedAssemblies);
+ Config.OnScriptCompile(ssea);
+
+#if CROSSPLATFORM || COREWIN
+ provider.BeforeEmitCompilation += Config.OnBeforeScriptCompilation;
+
+ cr = await provider.CompileAssemblyFromSourceAsync(cp, script, Config.CompilerSettings.CultureInfo, cancellationToken);
+#else
+ cr = provider.CompileAssemblyFromSource(cp, script);
+#endif
+ Assembly = null;
+ Instance = null;
+
+ if (cr.Errors.Count != 0) // Compile errors
+ return cr; // return false;
+
+ FAssemblyCache.TryAdd(assemblyHash, cr.CompiledAssembly);
+
+ Assembly = cr.CompiledAssembly;
+ var reportScript = Assembly.CreateInstance("FastReport.ReportScript");
+ InitInstance(reportScript);
+ return cr;
+ }
+ }
+
+
+ ///
+ /// Returns true if recompilation is successful
+ ///
+ private async Task TryRecompileAsync(CompilerParameters cp, CompilerResults oldResult, CancellationToken ct)
+ {
+ List additionalAssemblies = new List(4);
+
+ foreach (CompilerError ce in oldResult.Errors)
+ {
+ if (ce.ErrorNumber == "CS0012") // missing reference on assembly
+ {
+ // try to add reference
+ try
+ {
+ // in .Net Core compiler will return other quotes
+#if CROSSPLATFORM || COREWIN
+ const string quotes = "\'";
+#else
+ const string quotes = "\"";
+#endif
+ const string pattern = quotes + @"(\S{1,}),";
+ Regex regex = new Regex(pattern, RegexOptions.Compiled);
+ string assemblyName = regex.Match(ce.ErrorText).Groups[1].Value; // Groups[1] include string without quotes and , symbols
+ if (!additionalAssemblies.Contains(assemblyName))
+ additionalAssemblies.Add(assemblyName);
+ continue;
+ }
+ catch { }
+ }
+ }
+
+ if (additionalAssemblies.Count > 0) // need recompile
+ {
+ // try to load missing assemblies
+ foreach (string assemblyName in additionalAssemblies)
+ {
+ AddReferencedAssembly(cp.ReferencedAssemblies, currentFolder, assemblyName);
+ }
+
+ return await InternalCompileAsync(cp, ct);
+ }
+
+ return oldResult;
+ }
+
+ public void Dispose()
+ {
+ semaphoreSlim.Dispose();
+ }
+ }
+}
diff --git a/FastReport.Base/Code/AssemblyDescriptor.cs b/FastReport.Base/Code/AssemblyDescriptor.cs
index 67981c65..9a0edbbb 100644
--- a/FastReport.Base/Code/AssemblyDescriptor.cs
+++ b/FastReport.Base/Code/AssemblyDescriptor.cs
@@ -13,8 +13,6 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
using System.Collections.Concurrent;
using FastReport.Data;
using FastReport.Engine;
@@ -25,7 +23,7 @@
namespace FastReport.Code
{
- partial class AssemblyDescriptor : IDisposable
+ partial class AssemblyDescriptor
{
private static readonly ConcurrentDictionary FAssemblyCache;
private readonly StringBuilder scriptText;
@@ -36,9 +34,6 @@ partial class AssemblyDescriptor : IDisposable
private const string shaKey = "FastReportCode";
private readonly static object compileLocker;
private readonly string currentFolder;
-#if ASYNC
- private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
-#endif
public Assembly Assembly { get; private set; }
@@ -169,27 +164,6 @@ private static void AddFastReportAssemblies(StringCollection assemblies)
}
}
-#if ASYNC
- private static async ValueTask AddFastReportAssemblies(StringCollection assemblies, CancellationToken token)
- {
- foreach (Assembly assembly in RegisteredObjects.Assemblies)
- {
- string aLocation = assembly.Location;
-#if CROSSPLATFORM || COREWIN
- if (aLocation == "")
- {
- // try fix SFA in FastReport.Compat
- string fixedReference = await CodeDomProvider.TryFixReferenceInSingeFileAppAsync(assembly, token);
- if (!string.IsNullOrEmpty(fixedReference))
- aLocation = fixedReference;
- }
-#endif
- if (!ContainsAssembly(assemblies, aLocation))
- assemblies.Add(aLocation);
- }
- }
-#endif
-
private void AddReferencedAssemblies(StringCollection assemblies, string defaultPath)
{
for (int i = 0; i < Report.ReferencedAssemblies.Length; i++)
@@ -414,27 +388,6 @@ public void Compile()
}
}
-#if ASYNC
- public async Task CompileAsync(CancellationToken token = default)
- {
- if (needCompile)
- {
- await semaphoreSlim.WaitAsync(token);
-
- if (needCompile)
- {
- try
- {
- await InternalCompileAsync(token);
- }
- finally
- {
- semaphoreSlim.Release();
- }
- }
- }
- }
-#endif
private void InternalCompile()
{
@@ -460,39 +413,6 @@ private void InternalCompile()
throw new CompilerException(errors);
}
-#if ASYNC
- private async Task InternalCompileAsync(CancellationToken cancellationToken)
- {
- CompilerParameters cp = await GetCompilerParametersAsync(cancellationToken);
-
- if (Config.WebMode &&
- Config.EnableScriptSecurity &&
- Config.ScriptSecurityProps.AddStubClasses)
- AddStubClasses();
-
- string errors = string.Empty;
- CompilerResults cr = await InternalCompileAsync(cp, cancellationToken);
- bool success = CheckCompileResult(cr);
- for (int i = 0; !success && i < Config.CompilerSettings.RecompileCount; i++)
- {
- cr = await TryRecompileAsync(cp, cr, cancellationToken);
- success = CheckCompileResult(cr);
- }
-
- if (cr != null)
- HandleCompileErrors(cr, out errors);
-
- if (!success && errors != string.Empty)
- throw new CompilerException(errors);
- }
-
- private static bool CheckCompileResult(CompilerResults result)
- {
- // if result == null => was found in cache
- return result == null || result.Errors.Count == 0;
- }
-#endif
-
private CompilerParameters GetCompilerParameters()
{
// configure compiler options
@@ -507,21 +427,6 @@ private CompilerParameters GetCompilerParameters()
return cp;
}
-#if ASYNC
- private async Task GetCompilerParametersAsync(CancellationToken ct)
- {
- // configure compiler options
- CompilerParameters cp = new CompilerParameters();
- await AddFastReportAssemblies(cp.ReferencedAssemblies, ct); // 2
- AddReferencedAssemblies(cp.ReferencedAssemblies, currentFolder); // 9
- ReviewReferencedAssemblies(cp.ReferencedAssemblies);
- cp.GenerateInMemory = true;
- // sometimes the system temp folder is not accessible...
- if (Config.TempFolder != null)
- cp.TempFiles = new TempFileCollection(Config.TempFolder, false);
- return cp;
- }
-#endif
private string GetAssemblyHash(CompilerParameters cp)
{
@@ -588,51 +493,6 @@ private bool TryInternalCompile(CompilerParameters cp, out CompilerResults cr)
}
}
-#if ASYNC && (CROSSPLATFORM || COREWIN)
- ///
- /// Returns true, if compilation is successful
- ///
- private async ValueTask InternalCompileAsync(CompilerParameters cp, CancellationToken cancellationToken)
- {
- CompilerResults cr;
- // find assembly in cache
- string assemblyHash = GetAssemblyHash(cp);
- Assembly cachedAssembly;
- if (FAssemblyCache.TryGetValue(assemblyHash, out cachedAssembly))
- {
- Assembly = cachedAssembly;
- var reportScript = Assembly.CreateInstance("FastReport.ReportScript");
- InitInstance(reportScript);
- cr = null;
- return cr; // return true;
- }
-
- // compile report scripts
- using (CodeDomProvider provider = Report.CodeHelper.GetCodeProvider())
- {
- string script = scriptText.ToString();
- ScriptSecurityEventArgs ssea = new ScriptSecurityEventArgs(Report, script, Report.ReferencedAssemblies);
- Config.OnScriptCompile(ssea);
-
- provider.BeforeEmitCompilation += Config.OnBeforeScriptCompilation;
-
- cr = await provider.CompileAssemblyFromSourceAsync(cp, script, Config.CompilerSettings.CultureInfo, cancellationToken);
- Assembly = null;
- Instance = null;
-
- if (cr.Errors.Count != 0) // Compile errors
- return cr; // return false;
-
- FAssemblyCache.TryAdd(assemblyHash, cr.CompiledAssembly);
-
- Assembly = cr.CompiledAssembly;
- var reportScript = Assembly.CreateInstance("FastReport.ReportScript");
- InitInstance(reportScript);
- return cr;
- }
- }
-#endif
-
private string ReplaceExpression(string error, TextObjectBase text)
{
@@ -811,53 +671,6 @@ private bool TryRecompile(CompilerParameters cp, ref CompilerResults cr)
return false;
}
-#if ASYNC
- ///
- /// Returns true if recompilation is successful
- ///
- private async Task TryRecompileAsync(CompilerParameters cp, CompilerResults oldResult, CancellationToken ct)
- {
- List additionalAssemblies = new List(4);
-
- foreach (CompilerError ce in oldResult.Errors)
- {
- if (ce.ErrorNumber == "CS0012") // missing reference on assembly
- {
- // try to add reference
- try
- {
- // in .Net Core compiler will return other quotes
-#if CROSSPLATFORM || COREWIN
- const string quotes = "\'";
-#else
- const string quotes = "\"";
-#endif
- const string pattern = quotes + @"(\S{1,}),";
- Regex regex = new Regex(pattern, RegexOptions.Compiled);
- string assemblyName = regex.Match(ce.ErrorText).Groups[1].Value; // Groups[1] include string without quotes and , symbols
- if (!additionalAssemblies.Contains(assemblyName))
- additionalAssemblies.Add(assemblyName);
- continue;
- }
- catch { }
- }
- }
-
- if (additionalAssemblies.Count > 0) // need recompile
- {
- // try to load missing assemblies
- foreach (string assemblyName in additionalAssemblies)
- {
- AddReferencedAssembly(cp.ReferencedAssemblies, currentFolder, assemblyName);
- }
-
- return await InternalCompileAsync(cp, ct);
- }
-
- return oldResult;
- }
-#endif
-
public void InitInstance(object instance)
{
this.Instance = instance;
@@ -900,12 +713,6 @@ public object InvokeMethod(string name, object[] parms)
}
}
- public void Dispose()
- {
-#if ASYNC
- semaphoreSlim.Dispose();
-#endif
- }
public AssemblyDescriptor(Report report, string scriptText)
{
diff --git a/FastReport.Base/ContainerObject.Async.cs b/FastReport.Base/ContainerObject.Async.cs
new file mode 100644
index 00000000..11de54fe
--- /dev/null
+++ b/FastReport.Base/ContainerObject.Async.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport
+{
+ ///
+ /// Container object that may contain child objects.
+ ///
+ public partial class ContainerObject : ReportComponentBase, IParent
+ {
+ #region Report engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ foreach (ReportComponentBase obj in Objects)
+ {
+ await obj.GetDataAsync(cancellationToken);
+ obj.OnAfterData();
+
+ // break the component if it is of BreakableComponent an has non-empty BreakTo property
+ if (obj is BreakableComponent && (obj as BreakableComponent).BreakTo != null &&
+ (obj as BreakableComponent).BreakTo.GetType() == obj.GetType())
+ (obj as BreakableComponent).Break((obj as BreakableComponent).BreakTo);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/FastReport.Base/CrossView/CrossViewObject.Async.cs b/FastReport.Base/CrossView/CrossViewObject.Async.cs
new file mode 100644
index 00000000..4c5335c7
--- /dev/null
+++ b/FastReport.Base/CrossView/CrossViewObject.Async.cs
@@ -0,0 +1,24 @@
+using System;
+using FastReport.Table;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport.CrossView
+{
+ ///
+ /// Represents the crossview object that is used to print cube slice or slicegrid.
+ ///
+ public partial class CrossViewObject : TableBase
+ {
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+
+ #endregion
+ }
+}
diff --git a/FastReport.Base/CrossView/CrossViewObject.cs b/FastReport.Base/CrossView/CrossViewObject.cs
index f33d8902..3aa72a8b 100644
--- a/FastReport.Base/CrossView/CrossViewObject.cs
+++ b/FastReport.Base/CrossView/CrossViewObject.cs
@@ -443,8 +443,12 @@ public override void SaveState()
///
public override void GetData()
{
-
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
if (Data.SourceAssigned)
{
diff --git a/FastReport.Base/Data/DataConnectionBase.cs b/FastReport.Base/Data/DataConnectionBase.cs
index 93bf2750..184c9bea 100644
--- a/FastReport.Base/Data/DataConnectionBase.cs
+++ b/FastReport.Base/Data/DataConnectionBase.cs
@@ -207,7 +207,7 @@ private void GetDBObjectNames(string name, List list)
}
}
- private string PrepareSelectCommand(string selectCommand, string tableName, DbConnection connection)
+ protected string PrepareSelectCommand(string selectCommand, string tableName, DbConnection connection)
{
if (String.IsNullOrEmpty(selectCommand))
{
@@ -216,7 +216,7 @@ private string PrepareSelectCommand(string selectCommand, string tableName, DbCo
return selectCommand;
}
- private TableDataSource FindTableDataSource(DataTable table)
+ protected TableDataSource FindTableDataSource(DataTable table)
{
foreach (TableDataSource c in Tables)
{
diff --git a/FastReport.Base/Data/Dictionary.cs b/FastReport.Base/Data/Dictionary.cs
index 2c731e4f..edbc88d9 100644
--- a/FastReport.Base/Data/Dictionary.cs
+++ b/FastReport.Base/Data/Dictionary.cs
@@ -237,7 +237,7 @@ internal void RegisterDataTable(DataTable table, string referenceName, bool enab
///
/// The DataView to register.
/// The name of the data object.
- /// Determines wheter to enable the object or not.
+ /// Determines whether to enable the object or not.
///
/// This method is for internal use only.
///
@@ -650,10 +650,13 @@ public override void Serialize(FRWriter writer)
{
writer.ItemName = ClassName;
ObjectCollection childObjects = ChildObjects;
- foreach (Base c in childObjects)
+ if (writer.SaveChildren)
{
- if (c is Parameter || c is Total || c is CubeSourceBase || (c is DataComponentBase && (c as DataComponentBase).Enabled))
- writer.Write(c);
+ foreach (Base c in childObjects)
+ {
+ if (c is Parameter || c is Total || c is CubeSourceBase || (c is DataComponentBase && (c as DataComponentBase).Enabled))
+ writer.Write(c);
+ }
}
}
@@ -720,6 +723,16 @@ public void Load(string fileName)
///
/// Another dictionary to merge the data from.
public void Merge(Dictionary source)
+ {
+ Merge(source, false);
+ }
+
+ ///
+ /// Merges this dictionary with another Dictionary.
+ ///
+ /// Another dictionary to merge the data from.
+ /// Determines whether to disable the merge of the totals and parameters.
+ public void Merge(Dictionary source, bool mergeOnlyDataSource)
{
// Report object is needed to handle save/load of dictionary correctly.
// Some dictionary objects (such as relations) may contain references to other objects.
@@ -733,18 +746,27 @@ public void Merge(Dictionary source)
foreach (Base c in clone.ChildObjects)
{
+ if ((c is Total || c is Parameter) && mergeOnlyDataSource)
+ continue;
+
Base my = FindByName(c.Name);
if (my != null)
{
- foreach (PageBase page in Report.Pages)
+ if (c is DataSourceBase dataSourceBase)
{
- if (page is ReportPage)
- foreach (Base band in (page as ReportPage).AllObjects)
+ foreach (PageBase page in Report.Pages)
+ {
+ if (page is ReportPage)
{
- if (band is DataBand dataBand && dataBand.DataSource != null && (my.AllObjects.Contains(dataBand.DataSource) || dataBand.DataSource.Name == my.Name
- || dataBand.DataSource == my))
- dataBand.DataSource = (DataSourceBase)clone.FindByName(dataBand.DataSource.Name);
+ foreach (Base obj in (page as ReportPage).AllObjects)
+ {
+ if (obj is IContainDataSource dataObj)
+ {
+ dataObj.UpdateDataSourceRef(dataSourceBase);
+ }
+ }
}
+ }
}
my.Dispose();
}
@@ -755,7 +777,7 @@ public void Merge(Dictionary source)
ReRegisterData();
}
}
- #endregion
+#endregion
#region IParent Members
///
diff --git a/FastReport.Base/DataBand.cs b/FastReport.Base/DataBand.cs
index 95562db7..855af16c 100644
--- a/FastReport.Base/DataBand.cs
+++ b/FastReport.Base/DataBand.cs
@@ -15,7 +15,7 @@ namespace FastReport
/// property if you want to filter data rows. The
/// property can be used to sort data rows.
///
- public partial class DataBand : BandBase
+ public partial class DataBand : BandBase, IContainDataSource
{
#region Fields
private DataHeaderBand header;
@@ -351,6 +351,14 @@ private void DataSource_Disposed(object sender, EventArgs e)
{
dataSource = null;
}
+
+ void IContainDataSource.UpdateDataSourceRef(DataSourceBase newRefDatasource)
+ {
+ if (newRefDatasource != null && (newRefDatasource.Name == DataSource?.Name || newRefDatasource == DataSource))
+ {
+ DataSource = newRefDatasource;
+ }
+ }
#endregion
#region Protected Methods
diff --git a/FastReport.Base/Engine/ReportEngine.Async.cs b/FastReport.Base/Engine/ReportEngine.Async.cs
new file mode 100644
index 00000000..c8252d1b
--- /dev/null
+++ b/FastReport.Base/Engine/ReportEngine.Async.cs
@@ -0,0 +1,72 @@
+using FastReport.Utils;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Engine
+{
+ ///
+ /// Represents the report engine.
+ ///
+ public partial class ReportEngine
+ {
+
+ #region Private Methods
+
+ private async Task RunReportPagesAsync(ReportPage page, CancellationToken cancellationToken)
+ {
+ OnStateChanged(Report, EngineState.ReportStarted);
+
+ if (page == null)
+ await RunReportPagesAsync(cancellationToken);
+ else
+ await RunReportPageAsync(page, cancellationToken);
+
+ OnStateChanged(Report, EngineState.ReportFinished);
+ }
+
+ #endregion Private Methods
+
+ #region Internal Methods
+
+
+ internal Task RunAsync(bool runDialogs, bool append, bool resetDataState, CancellationToken cancellationToken)
+ {
+ return RunAsync(runDialogs, append, resetDataState, null, cancellationToken);
+ }
+
+ internal async Task RunAsync(bool runDialogs, bool append, bool resetDataState, ReportPage page, CancellationToken cancellationToken)
+ {
+ try
+ {
+ RunPhase1(resetDataState);
+ if (runDialogs && !(await RunDialogsAsync()))
+ return false;
+ await RunPhase2Async(append, page, cancellationToken);
+ }
+ finally
+ {
+ RunFinished();
+ }
+ return true;
+ }
+
+ internal async Task RunPhase2Async(bool append, ReportPage page, CancellationToken cancellationToken)
+ {
+ Config.ReportSettings.OnStartProgress(Report);
+ PrepareToFirstPass(append);
+ await RunReportPagesAsync(page, cancellationToken);
+
+ ResetLogicalPageNumber();
+ if (Report.DoublePass && !Report.Aborted)
+ {
+ finalPass = true;
+ PrepareToSecondPass();
+ await RunReportPagesAsync(page, cancellationToken);
+ }
+ }
+
+ #endregion Internal Methods
+ }
+}
diff --git a/FastReport.Base/Engine/ReportEngine.Bands.Async.cs b/FastReport.Base/Engine/ReportEngine.Bands.Async.cs
new file mode 100644
index 00000000..3ed3615e
--- /dev/null
+++ b/FastReport.Base/Engine/ReportEngine.Bands.Async.cs
@@ -0,0 +1,316 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Engine
+{
+ public partial class ReportEngine
+ {
+ #region Private Methods
+
+ private async Task PrepareBandAsync(BandBase band, bool getData, CancellationToken cancellationToken)
+ {
+ if (band.Visible)
+ {
+ if (getData)
+ {
+ await band.GetDataAsync(cancellationToken);
+ }
+ PrepareBandShared(band);
+ }
+ }
+
+ private async Task CalcHeightAsync(BandBase band, CancellationToken cancellationToken)
+ {
+ // band is already prepared, its Height is ready to use
+ if (band.IsRunning)
+ return band.Height;
+
+ band.SaveState();
+ try
+ {
+ await PrepareBandAsync(band, true, cancellationToken);
+ return band.Height;
+ }
+ finally
+ {
+ band.RestoreState();
+ }
+ }
+
+ private async Task AddToOutputBandAsync(BandBase band, bool getData, CancellationToken cancellationToken)
+ {
+ band.SaveState();
+
+ try
+ {
+ await PrepareBandAsync(band, getData, cancellationToken);
+
+ if (band.Visible)
+ {
+ outputBand.SetRunning(true);
+
+ BandBase cloneBand = CloneBand(band);
+ cloneBand.Left = CurX;
+ cloneBand.Top = CurY;
+ cloneBand.Parent = outputBand;
+
+ CurY += cloneBand.Height;
+ }
+ }
+ finally
+ {
+ band.RestoreState();
+ }
+ }
+
+ private async Task ShowBandToPreparedPagesAsync(BandBase band, bool getData, CancellationToken cancellationToken)
+ {
+ // handle "StartNewPage". Skip if it's the first row, avoid empty first page.
+ if ((band.StartNewPage && !(band.Parent is PageHeaderBand || band.Parent is PageFooterBand)) && band.FlagUseStartNewPage && (band.RowNo != 1 || band.FirstRowStartsNewPage) &&
+ !band.Repeated)
+ {
+ EndColumn();
+ }
+
+ band.SaveState();
+ try
+ {
+ await PrepareBandAsync(band, getData, cancellationToken);
+
+ if (band.Visible)
+ {
+ if (BandHasHardPageBreaks(band))
+ {
+ foreach (var b in SplitHardPageBreaks(band))
+ {
+ if (b.StartNewPage)
+ EndColumn();
+ await AddToPreparedPagesAsync(b, cancellationToken);
+ }
+ }
+ else
+ {
+ await AddToPreparedPagesAsync(band, cancellationToken);
+ }
+ }
+ }
+ finally
+ {
+ band.RestoreState();
+ }
+ }
+
+
+ public async Task ShowBandAsync(BandBase band, CancellationToken cancellationToken)
+ {
+ if (band != null)
+ for (int i = 0; i < band.RepeatBandNTimes; i++)
+ await ShowBandAsync(band, true, cancellationToken);
+ }
+
+ private async Task ShowBandAsync(BandBase band, bool getData, CancellationToken cancellationToken)
+ {
+ if (band == null)
+ return;
+
+ BandBase saveCurBand = curBand;
+ curBand = band;
+
+ try
+ {
+ // do we need to keep child?
+ ChildBand child = band.Child;
+ bool showChild = child != null && !(band is DataBand && child.CompleteToNRows > 0) && !child.FillUnusedSpace &&
+ !(band is DataBand && child.PrintIfDatabandEmpty);
+ if (showChild && band.KeepChild)
+ {
+ StartKeep(band);
+ }
+
+ if (outputBand != null)
+ {
+ await AddToOutputBandAsync(band, getData, cancellationToken);
+ }
+ else
+ {
+ await ShowBandToPreparedPagesAsync(band, getData, cancellationToken);
+ }
+
+ ProcessTotals(band);
+ if (band.Visible)
+ {
+ await RenderOuterSubreportsAsync(band, cancellationToken);
+ }
+
+ // show child band. Skip if child is used to fill empty space: it was processed already
+ if (showChild)
+ {
+ await ShowBandAsync(child, cancellationToken);
+ if (band.KeepChild)
+ {
+ EndKeep();
+ }
+ }
+ }
+ finally
+ {
+ curBand = saveCurBand;
+ }
+ }
+
+ internal async Task AddToPreparedPagesAsync(BandBase band, CancellationToken cancellationToken)
+ {
+ bool isReportSummary = band is ReportSummaryBand;
+
+ // check if band is service band (e.g. page header/footer/overlay).
+ BandBase mainBand = band;
+ // for child bands, check its parent band.
+ if (band is ChildBand)
+ {
+ mainBand = (band as ChildBand).GetTopParentBand;
+ }
+ bool isPageBand = mainBand is PageHeaderBand || mainBand is PageFooterBand || mainBand is OverlayBand;
+ bool isColumnBand = mainBand is ColumnHeaderBand || mainBand is ColumnFooterBand;
+
+ // check if we have enough space for a band.
+ bool checkFreeSpace = !isPageBand && !isColumnBand && band.FlagCheckFreeSpace;
+ if (checkFreeSpace && FreeSpace < band.Height)
+ {
+ // we don't have enough space. What should we do?
+ // - if band can break, break it
+ // - if band cannot break, check the band height:
+ // - it's the first row of a band and is bigger than page: break it immediately.
+ // - in other case, add a new page/column and tell the band that it must break next time.
+ if (band.CanBreak || band.FlagMustBreak || (band.AbsRowNo == 1 && band.Height > PageHeight - PageFooterHeight))
+ {
+ // since we don't show the column footer band in the EndLastPage, do it here.
+ if (isReportSummary)
+ {
+ ShowReprintFooters();
+ await ShowBandAsync(page.ColumnFooter, cancellationToken);
+ }
+ BreakBand(band);
+ return;
+ }
+ else
+ {
+ EndColumn();
+ band.FlagMustBreak = true;
+ await AddToPreparedPagesAsync(band, cancellationToken);
+ band.FlagMustBreak = false;
+ return;
+ }
+ }
+ else
+ {
+ // since we don't show the column footer band in the EndLastPage, do it here.
+ if (isReportSummary)
+ {
+ if ((band as ReportSummaryBand).KeepWithData)
+ {
+ EndKeep();
+ }
+ ShowReprintFooters(false);
+ await ShowBandAsync(page.ColumnFooter, cancellationToken);
+ }
+ }
+
+ // check if we have a child band with FillUnusedSpace flag
+ if (band.Child != null && band.Child.FillUnusedSpace)
+ {
+ // if we reprint a data/group footer, do not include the band height into calculation:
+ // it is already counted in FreeSpace
+ float bandHeight = band.Height;
+ if (band.Repeated)
+ {
+ bandHeight = 0;
+ }
+ while (FreeSpace - bandHeight - band.Child.Height >= 0)
+ {
+ float saveCurY = CurY;
+ await ShowBandAsync(band.Child, cancellationToken);
+ // nothing was printed, break to avoid an endless loop
+ if (CurY == saveCurY)
+ {
+ break;
+ }
+ }
+ }
+
+ // adjust the band location
+ if (band is PageFooterBand && !UnlimitedHeight)
+ {
+ CurY = PageHeight - GetBandHeightWithChildren(band);
+ }
+ if (!isPageBand)
+ {
+ band.Left += originX + CurX;
+ }
+ if (band.PrintOnBottom)
+ {
+ CurY = PageHeight - PageFooterHeight - ColumnFooterHeight;
+ // if PrintOnBottom is applied to a band like DataFooter, print it with all its child bands
+ // if PrintOnBottom is applied to a child band, print this band only.
+ if (band is ChildBand)
+ {
+ CurY -= band.Height;
+ }
+ else
+ {
+ CurY -= GetBandHeightWithChildren(band);
+ }
+ }
+ band.Top = CurY;
+
+ // shift the band and decrease its width when printing hierarchy
+ float saveLeft = band.Left;
+ float saveWidth = band.Width;
+ if (!isPageBand && !isColumnBand)
+ {
+ band.Left += hierarchyIndent;
+ band.Width -= hierarchyIndent;
+ }
+
+ // add outline
+ AddBandOutline(band);
+
+ // add bookmarks
+ band.AddBookmarks();
+
+ // put the band to prepared pages. Do not put page bands twice
+ // (this may happen when we render a subreport, or append a report to another one).
+ bool bandAdded = true;
+ bool bandAlreadyExists = false;
+ if (isPageBand)
+ {
+ if (band is ChildBand)
+ {
+ bandAlreadyExists = PreparedPages.ContainsBand(band.Name);
+ }
+ else
+ {
+ bandAlreadyExists = PreparedPages.ContainsBand(band.GetType());
+ }
+ }
+
+ if (!bandAlreadyExists)
+ {
+ bandAdded = PreparedPages.AddBand(band);
+ }
+
+ // shift CurY
+ if (bandAdded && !(mainBand is OverlayBand))
+ {
+ CurY += band.Height;
+ }
+
+ // set left&width back
+ band.Left = saveLeft;
+ band.Width = saveWidth;
+ }
+
+ #endregion Private Methods
+ }
+}
diff --git a/FastReport.Base/Engine/ReportEngine.Bands.cs b/FastReport.Base/Engine/ReportEngine.Bands.cs
index 53dd5d1a..4a6c5a21 100644
--- a/FastReport.Base/Engine/ReportEngine.Bands.cs
+++ b/FastReport.Base/Engine/ReportEngine.Bands.cs
@@ -21,12 +21,17 @@ private void PrepareBand(BandBase band, bool getData)
{
band.GetData();
}
- TranslateObjects(band);
- RenderInnerSubreports(band);
- band.CalcHeight();
+ PrepareBandShared(band);
}
}
+ private void PrepareBandShared(BandBase band)
+ {
+ TranslateObjects(band);
+ RenderInnerSubreports(band);
+ band.CalcHeight();
+ }
+
private float CalcHeight(BandBase band)
{
// band is already prepared, its Height is ready to use
diff --git a/FastReport.Base/Engine/ReportEngine.DataBands.Async.cs b/FastReport.Base/Engine/ReportEngine.DataBands.Async.cs
new file mode 100644
index 00000000..a458dc4c
--- /dev/null
+++ b/FastReport.Base/Engine/ReportEngine.DataBands.Async.cs
@@ -0,0 +1,291 @@
+using FastReport.Data;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Engine
+{
+ public partial class ReportEngine
+ {
+ #region Private Methods
+
+ private async Task RunDataBandAsync(DataBand dataBand, CancellationToken cancellationToken)
+ {
+ if (page.Columns.Count > 1 && Report.Engine.UnlimitedHeight)
+ dataBand.Columns.Count = page.Columns.Count;
+
+ dataBand.InitDataSource();
+ dataBand.DataSource.First();
+
+ int rowCount = dataBand.DataSource.RowCount;
+ if (dataBand.IsDatasourceEmpty && dataBand.PrintIfDatasourceEmpty)
+ rowCount = 1;
+ if (dataBand.CollectChildRows && rowCount > 1)
+ rowCount = 1;
+ if (dataBand.MaxRows > 0 && rowCount > dataBand.MaxRows)
+ rowCount = dataBand.MaxRows;
+
+ bool keepFirstRow = NeedKeepFirstRow(dataBand);
+ bool keepLastRow = NeedKeepLastRow(dataBand);
+
+ await RunDataBandAsync(dataBand, rowCount, keepFirstRow, keepLastRow, cancellationToken);
+
+ // do not leave the datasource in EOF state to allow print something in the footer
+ dataBand.DataSource.Prior();
+ }
+
+ private async Task RunDataBandAsync(DataBand dataBand, int rowCount, bool keepFirstRow, bool keepLastRow, CancellationToken cancellationToken)
+ {
+ if (dataBand.IsHierarchical)
+ {
+ await ShowHierarchyAsync(dataBand, rowCount, cancellationToken);
+ return;
+ }
+
+ bool isFirstRow = true;
+ bool someRowsPrinted = false;
+ dataBand.RowNo = 0;
+ dataBand.IsFirstRow = false;
+ dataBand.IsLastRow = false;
+
+ // check if we have only one data row that should be kept with both header and footer
+ bool oneRow = rowCount == 1 && keepFirstRow && keepLastRow;
+
+ // cycle through records
+ for (int i = 0; i < rowCount; i++)
+ {
+ bool isLastRow = i == rowCount - 1;
+ if (!dataBand.IsDetailEmpty())
+ {
+ dataBand.RowNo++;
+ dataBand.AbsRowNo++;
+ dataBand.IsFirstRow = isFirstRow;
+ dataBand.IsLastRow = isLastRow;
+
+ // keep header
+ if (isFirstRow && keepFirstRow)
+ StartKeep(dataBand);
+
+ // keep together
+ if (isFirstRow && dataBand.KeepTogether)
+ StartKeep(dataBand);
+
+ // keep detail
+ if (dataBand.KeepDetail)
+ StartKeep(dataBand);
+
+ // show header
+ if (isFirstRow)
+ await ShowDataHeaderAsync(dataBand, cancellationToken);
+
+ // keep footer
+ if (isLastRow && keepLastRow && dataBand.IsDeepmostDataBand)
+ StartKeep(dataBand);
+
+ // start block event
+ if (isFirstRow)
+ OnStateChanged(dataBand, EngineState.BlockStarted);
+
+ // show band
+ await ShowDataBandAsync(dataBand, rowCount, cancellationToken);
+
+ // end keep header
+ if (isFirstRow && keepFirstRow && !oneRow)
+ EndKeep();
+
+ // end keep footer
+ if (isLastRow && keepLastRow && dataBand.IsDeepmostDataBand)
+ CheckKeepFooter(dataBand);
+
+ // show sub-bands
+ await RunBandsAsync(dataBand.Bands, cancellationToken);
+
+ // up the outline
+ OutlineUp(dataBand);
+
+ // end keep detail
+ if (dataBand.KeepDetail)
+ EndKeep();
+
+ isFirstRow = false;
+ someRowsPrinted = true;
+
+ if (dataBand.Columns.Count > 1)
+ break;
+ }
+
+ dataBand.DataSource.Next();
+ if (Report.Aborted)
+ break;
+ }
+
+ // complete upto N rows
+ ChildBand child = dataBand.Child;
+ if (child != null && child.CompleteToNRows > rowCount)
+ {
+ for (int i = 0; i < child.CompleteToNRows - rowCount; i++)
+ {
+ child.RowNo = rowCount + i + 1;
+ child.AbsRowNo = rowCount + i + 1;
+ await ShowBandAsync(child, cancellationToken);
+ }
+ }
+ // print child if databand is empty
+ if (child != null && child.PrintIfDatabandEmpty && dataBand.IsDatasourceEmpty)
+ {
+ await ShowBandAsync(child, cancellationToken);
+ }
+
+ if (someRowsPrinted)
+ {
+ // finish block event
+ OnStateChanged(dataBand, EngineState.BlockFinished);
+
+ // show footer
+ await ShowDataFooterAsync(dataBand, cancellationToken);
+
+ // end KeepTogether
+ if (dataBand.KeepTogether)
+ EndKeep();
+
+ // end KeepLastRow
+ if (keepLastRow)
+ EndKeep();
+ }
+ }
+
+ private async Task ShowDataBandAsync(DataBand dataBand, int rowCount, CancellationToken cancellationToken)
+ {
+ if (dataBand.Columns.Count > 1)
+ {
+ dataBand.Width = dataBand.Columns.ActualWidth;
+ await RenderMultiColumnBandAsync(dataBand, rowCount, cancellationToken);
+ }
+ else
+ {
+ if (dataBand.ResetPageNumber && (dataBand.FirstRowStartsNewPage || dataBand.RowNo > 1))
+ ResetLogicalPageNumber();
+ if (dataBand.Footer != null && dataBand.CanBreak)
+ if (dataBand.Footer.KeepWithData && dataBand.Footer.Height + dataBand.Height > FreeSpace)
+ {
+ dataBand.AddLastToFooter(dataBand.Footer);
+ }
+ await ShowBandAsync(dataBand, cancellationToken);
+ }
+ }
+
+ private async Task RenderMultiColumnBandAsync(DataBand dataBand, int rowCount, CancellationToken cancellationToken)
+ {
+ if (dataBand.Columns.Layout == ColumnLayout.AcrossThenDown)
+ RenderBandAcrossThenDown(dataBand, rowCount);
+ else
+ {
+ DataSourceBase dataSource = dataBand.DataSource;
+ int saveRow = dataSource.CurrentRowNo;
+
+ // calc height of each data row. This list is shared across RenderBandDownThenAcross calls.
+ Hashtable heights = new Hashtable();
+ for (int i = 0; i < rowCount; i++)
+ {
+ dataSource.CurrentRowNo = i + saveRow;
+ heights[i + saveRow] = await CalcHeightAsync(dataBand, cancellationToken);
+ }
+
+ dataSource.CurrentRowNo = saveRow;
+ while (rowCount > 0)
+ {
+ rowCount = RenderBandDownThenAcross(dataBand, rowCount, heights);
+ }
+ }
+ }
+
+ private async Task ShowHierarchyAsync(DataBand dataBand, HierarchyItem rootItem, int level, string fullRowNo, CancellationToken cancellationToken)
+ {
+ int saveLevel = hierarchyLevel;
+ float saveIndent = hierarchyIndent;
+ string saveRowNo = hierarchyRowNo;
+ hierarchyLevel = level;
+ hierarchyIndent = dataBand.Indent * (level - 1);
+
+ try
+ {
+ if (rootItem.items.Count > 0)
+ {
+ await ShowBandAsync(dataBand.Header, cancellationToken);
+
+ int rowNo = 0;
+ foreach (HierarchyItem item in rootItem.items)
+ {
+ rowNo++;
+ dataBand.RowNo = rowNo;
+ dataBand.AbsRowNo++;
+ dataBand.DataSource.CurrentRowNo = item.rowNo;
+ hierarchyRowNo = fullRowNo + rowNo.ToString();
+
+ // show the main hierarchy band
+ await ShowBandAsync(dataBand, cancellationToken);
+
+ // show sub-bands if any
+ await RunBandsAsync(dataBand.Bands, cancellationToken);
+
+ await ShowHierarchyAsync(dataBand, item, level + 1, hierarchyRowNo + ".", cancellationToken);
+
+ // up the outline
+ OutlineUp(dataBand);
+ }
+
+ await ShowBandAsync(dataBand.Footer, cancellationToken);
+ }
+ }
+ finally
+ {
+ hierarchyLevel = saveLevel;
+ hierarchyIndent = saveIndent;
+ hierarchyRowNo = saveRowNo;
+ }
+ }
+
+ private async Task ShowHierarchyAsync(DataBand dataBand, int rowCount, CancellationToken cancellationToken)
+ {
+ HierarchyItem rootItem = MakeHierarchy(dataBand, rowCount);
+ if (rootItem == null)
+ return;
+
+ await ShowHierarchyAsync(dataBand, rootItem, 1, "", cancellationToken);
+ }
+
+ private async Task ShowDataHeaderAsync(DataBand dataBand, CancellationToken cancellationToken)
+ {
+ DataHeaderBand header = dataBand.Header;
+ if (header != null)
+ {
+ await ShowBandAsync(header, cancellationToken);
+ if (header.RepeatOnEveryPage)
+ AddReprint(header);
+ }
+
+ DataFooterBand footer = dataBand.Footer;
+ if (footer != null)
+ {
+ if (footer.RepeatOnEveryPage)
+ AddReprint(footer);
+ }
+ }
+
+ private async Task ShowDataFooterAsync(DataBand dataBand, CancellationToken cancellationToken)
+ {
+ dataBand.DataSource.Prior();
+
+ DataFooterBand footer = dataBand.Footer;
+ RemoveReprint(footer);
+ await ShowBandAsync(footer, cancellationToken);
+ RemoveReprint(dataBand.Header);
+
+ dataBand.DataSource.Next();
+ }
+
+ #endregion Private Methods
+ }
+}
diff --git a/FastReport.Base/Engine/ReportEngine.Groups.Async.cs b/FastReport.Base/Engine/ReportEngine.Groups.Async.cs
new file mode 100644
index 00000000..4159b6fe
--- /dev/null
+++ b/FastReport.Base/Engine/ReportEngine.Groups.Async.cs
@@ -0,0 +1,162 @@
+using FastReport.Data;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Engine
+{
+ public partial class ReportEngine
+ {
+ #region Private Methods
+
+ private async Task ShowDataHeaderAsync(GroupHeaderBand groupBand, CancellationToken cancellationToken)
+ {
+ groupBand.RowNo = 0;
+
+ DataHeaderBand header = groupBand.Header;
+ if (header != null)
+ {
+ await ShowBandAsync(header, cancellationToken);
+ if (header.RepeatOnEveryPage)
+ AddReprint(header);
+ }
+
+ DataFooterBand footer = groupBand.Footer;
+ if (footer != null)
+ {
+ if (footer.RepeatOnEveryPage)
+ AddReprint(footer);
+ }
+ }
+
+ private async Task ShowDataFooterAsync(GroupHeaderBand groupBand, CancellationToken cancellationToken)
+ {
+ DataFooterBand footer = groupBand.Footer;
+ RemoveReprint(footer);
+ await ShowBandAsync(footer, cancellationToken);
+ RemoveReprint(groupBand.Header);
+ }
+
+ private async Task ShowGroupHeaderAsync(GroupHeaderBand header, CancellationToken cancellationToken)
+ {
+ header.AbsRowNo++;
+ header.RowNo++;
+
+ if (header.ResetPageNumber && (header.FirstRowStartsNewPage || header.RowNo > 1))
+ ResetLogicalPageNumber();
+ if (header.KeepTogether)
+ StartKeep(header);
+ if (header.KeepWithData)
+ StartKeep(header.GroupDataBand);
+
+ // start group event
+ OnStateChanged(header, EngineState.GroupStarted);
+
+ await ShowBandAsync(header, cancellationToken);
+ if (header.RepeatOnEveryPage)
+ AddReprint(header);
+
+ GroupFooterBand footer = header.GroupFooter;
+ if (footer != null)
+ {
+ if (footer.RepeatOnEveryPage)
+ AddReprint(footer);
+ }
+ }
+
+ private async Task ShowGroupFooterAsync(GroupHeaderBand header, CancellationToken cancellationToken)
+ {
+ // finish group event
+ OnStateChanged(header, EngineState.GroupFinished);
+
+ // rollback to previous data row to print the header condition in the footer.
+ DataBand dataBand = header.GroupDataBand;
+ DataSourceBase dataSource = dataBand.DataSource;
+ dataSource.Prior();
+
+ GroupFooterBand footer = header.GroupFooter;
+ if (footer != null)
+ {
+ footer.AbsRowNo++;
+ footer.RowNo++;
+ }
+ RemoveReprint(footer);
+ await ShowBandAsync(footer, cancellationToken);
+ RemoveReprint(header);
+
+ // restore current row
+ dataSource.Next();
+
+ OutlineUp(header);
+ if (header.KeepTogether)
+ EndKeep();
+ if (footer != null && footer.KeepWithData)
+ EndKeep();
+ }
+
+
+ private async Task ShowGroupTreeAsync(GroupTreeItem root, CancellationToken cancellationToken)
+ {
+ if (root.Band != null)
+ {
+ root.Band.GroupDataBand.DataSource.CurrentRowNo = root.RowNo;
+ await ShowGroupHeaderAsync(root.Band, cancellationToken);
+ }
+
+ if (root.Items.Count == 0)
+ {
+ if (root.RowCount != 0)
+ {
+ int rowCount = root.RowCount;
+ int maxRows = root.Band.GroupDataBand.MaxRows;
+ if (maxRows > 0 && rowCount > maxRows)
+ rowCount = maxRows;
+ bool keepFirstRow = NeedKeepFirstRow(root.Band);
+ bool keepLastRow = NeedKeepLastRow(root.Band.GroupDataBand);
+ await RunDataBandAsync(root.Band.GroupDataBand, rowCount, keepFirstRow, keepLastRow, cancellationToken);
+ }
+ }
+ else
+ {
+ await ShowDataHeaderAsync(root.FirstItem.Band, cancellationToken);
+
+ for (int i = 0; i < root.Items.Count; i++)
+ {
+ GroupTreeItem item = root.Items[i];
+ item.Band.IsFirstRow = i == 0;
+ item.Band.IsLastRow = i == root.Items.Count - 1;
+
+ await ShowGroupTreeAsync(item, cancellationToken);
+ if (Report.Aborted)
+ break;
+ }
+
+ await ShowDataFooterAsync(root.FirstItem.Band, cancellationToken);
+ }
+
+ if (root.Band != null)
+ await ShowGroupFooterAsync(root.Band, cancellationToken);
+ }
+
+ private async Task RunGroupAsync(GroupHeaderBand groupBand, CancellationToken cancellationToken)
+ {
+ DataSourceBase dataSource = groupBand.DataSource;
+ if (dataSource != null)
+ {
+ // init the datasource - set group conditions to sort data rows
+ groupBand.InitDataSource();
+
+ // show the group tree
+ await ShowGroupTreeAsync(MakeGroupTree(groupBand), cancellationToken);
+
+ // finalize the datasource, remove the group condition
+ // from the databand sort
+ groupBand.FinalizeDataSource();
+
+ // do not leave the datasource in EOF state to allow print something in the footer
+ dataSource.Prior();
+ }
+ }
+
+ #endregion Private Methods
+ }
+}
diff --git a/FastReport.Base/Engine/ReportEngine.Groups.cs b/FastReport.Base/Engine/ReportEngine.Groups.cs
index 84e7e3db..b1ca4612 100644
--- a/FastReport.Base/Engine/ReportEngine.Groups.cs
+++ b/FastReport.Base/Engine/ReportEngine.Groups.cs
@@ -11,8 +11,8 @@ private class GroupTreeItem
{
#region Fields
- private GroupHeaderBand band;
- private List items;
+ private readonly GroupHeaderBand band;
+ private readonly List items;
private int rowNo;
private int rowCount;
@@ -164,7 +164,7 @@ private void ShowGroupFooter(GroupHeaderBand header)
EndKeep();
}
- private void InitGroupItem(GroupHeaderBand header, GroupTreeItem curItem)
+ private static void InitGroupItem(GroupHeaderBand header, GroupTreeItem curItem)
{
while (header != null)
{
@@ -179,7 +179,7 @@ private void InitGroupItem(GroupHeaderBand header, GroupTreeItem curItem)
}
}
- private void CheckGroupItem(GroupHeaderBand header, GroupTreeItem curItem)
+ private static void CheckGroupItem(GroupHeaderBand header, GroupTreeItem curItem)
{
while (header != null)
{
diff --git a/FastReport.Base/Engine/ReportEngine.Pages.Async.cs b/FastReport.Base/Engine/ReportEngine.Pages.Async.cs
new file mode 100644
index 00000000..f2d7a011
--- /dev/null
+++ b/FastReport.Base/Engine/ReportEngine.Pages.Async.cs
@@ -0,0 +1,133 @@
+using FastReport.Utils;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Engine
+{
+ public partial class ReportEngine
+ {
+ #region Private Methods
+
+ private async Task RunReportPageAsync(ReportPage page, CancellationToken cancellationToken)
+ {
+ this.page = page;
+ InitReprint();
+ pageNameForRecalc = null;
+ this.page.OnStartPage(EventArgs.Empty);
+ bool previousPage = await StartFirstPageAsync(cancellationToken);
+ OnStateChanged(this.page, EngineState.ReportPageStarted);
+ OnStateChanged(this.page, EngineState.PageStarted);
+
+ DataBand keepSummaryBand = FindDeepmostDataBand(page);
+ if (keepSummaryBand != null)
+ keepSummaryBand.KeepSummary = true;
+
+ if (this.page.IsManualBuild)
+ this.page.OnManualBuild(EventArgs.Empty);
+ else
+ await RunBandsAsync(page.Bands, cancellationToken);
+
+ OnStateChanged(this.page, EngineState.PageFinished);
+ OnStateChanged(this.page, EngineState.ReportPageFinished);
+ EndLastPage(); // TODO
+ //recalculate unlimited
+ if (page.UnlimitedHeight || page.UnlimitedWidth)
+ {
+ PreparedPages.ModifyPageSize(page.Name);
+ if (previousPage && pageNameForRecalc != null)
+ PreparedPages.ModifyPageSize(pageNameForRecalc);
+ }
+ //recalculate unlimited
+ this.page.OnFinishPage(EventArgs.Empty);
+
+ if (this.page.BackPage)
+ {
+ PreparedPages.InterleaveWithBackPage(PreparedPages.CurPage);
+ }
+ }
+
+ private async Task RunReportPagesAsync(CancellationToken cancellationToken)
+ {
+#if TIMETRIAL
+ if (new DateTime($YEAR, $MONTH, $DAY) < System.DateTime.Now)
+ throw new Exception("The trial version is now expired!");
+#endif
+
+ for (int i = 0; i < Report.Pages.Count; i++)
+ {
+ ReportPage page = Report.Pages[i] as ReportPage;
+
+ // Calc and apply visible expression if needed.
+ if (page != null && !String.IsNullOrEmpty(page.VisibleExpression))
+ {
+ page.Visible = page.CalcVisibleExpression(page.VisibleExpression);
+ }
+
+ if (page != null && page.Visible && page.Subreport == null)
+ await RunReportPageAsync(page, cancellationToken);
+ if (Report.Aborted)
+ break;
+ }
+ }
+
+ private async Task RunBandsAsync(BandCollection bands, CancellationToken cancellationToken)
+ {
+ for (int i = 0; i < bands.Count; i++)
+ {
+ BandBase band = bands[i];
+ if (band is DataBand)
+ await RunDataBandAsync(band as DataBand, cancellationToken);
+ else if (band is GroupHeaderBand)
+ await RunGroupAsync(band as GroupHeaderBand, cancellationToken);
+ if (Report.Aborted)
+ break;
+ }
+ }
+
+ private async Task StartFirstPageAsync(CancellationToken cancellationToken)
+ {
+ var previousPage = StartFirstPageShared();
+
+ // show report title and page header
+ if (previousPage)
+ await ShowBandAsync(page.ReportTitle, cancellationToken);
+ else
+ {
+ if (page.Overlay != null)
+ await ShowBandAsync(page.Overlay, cancellationToken);
+ if (page.TitleBeforeHeader)
+ {
+ await ShowBandAsync(page.ReportTitle, cancellationToken);
+ ShowPageHeader();
+ }
+ else
+ {
+ ShowPageHeader();
+ await ShowBandAsync(page.ReportTitle, cancellationToken);
+ }
+ }
+
+ // show column header
+ columnStartY = CurY;
+ await ShowBandAsync(page.ColumnHeader, cancellationToken);
+
+ // calculate CurX before starting column event depending on Right to Left or Left to Right layout
+ if (Config.RightToLeft)
+ {
+ CurX = page.Columns.Positions[page.Columns.Positions.Count - 1] * Units.Millimeters;
+ }
+ else
+ {
+ CurX = page.Columns.Positions[0] * Units.Millimeters;
+ }
+
+ // start column event
+ OnStateChanged(page, EngineState.ColumnStarted);
+ ShowProgress();
+ return previousPage;
+ }
+
+ #endregion Private Methods
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/Engine/ReportEngine.Pages.cs b/FastReport.Base/Engine/ReportEngine.Pages.cs
index abb2918a..e88cbba3 100644
--- a/FastReport.Base/Engine/ReportEngine.Pages.cs
+++ b/FastReport.Base/Engine/ReportEngine.Pages.cs
@@ -123,6 +123,49 @@ private void ShowPageFooter(bool startPage)
}
private bool StartFirstPage()
+ {
+ var previousPage = StartFirstPageShared();
+
+ // show report title and page header
+ if (previousPage)
+ ShowBand(page.ReportTitle);
+ else
+ {
+ if (page.Overlay != null)
+ ShowBand(page.Overlay);
+ if (page.TitleBeforeHeader)
+ {
+ ShowBand(page.ReportTitle);
+ ShowPageHeader();
+ }
+ else
+ {
+ ShowPageHeader();
+ ShowBand(page.ReportTitle);
+ }
+ }
+
+ // show column header
+ columnStartY = CurY;
+ ShowBand(page.ColumnHeader);
+
+ // calculate CurX before starting column event depending on Right to Left or Left to Right layout
+ if (Config.RightToLeft)
+ {
+ CurX = page.Columns.Positions[page.Columns.Positions.Count - 1] * Units.Millimeters;
+ }
+ else
+ {
+ CurX = page.Columns.Positions[0] * Units.Millimeters;
+ }
+
+ // start column event
+ OnStateChanged(page, EngineState.ColumnStarted);
+ ShowProgress();
+ return previousPage;
+ }
+
+ private bool StartFirstPageShared()
{
page.InitializeComponents();
@@ -221,42 +264,6 @@ private bool StartFirstPage()
OutlineRoot();
AddPageOutline();
- // show report title and page header
- if (previousPage)
- ShowBand(page.ReportTitle);
- else
- {
- if (page.Overlay != null)
- ShowBand(page.Overlay);
- if (page.TitleBeforeHeader)
- {
- ShowBand(page.ReportTitle);
- ShowPageHeader();
- }
- else
- {
- ShowPageHeader();
- ShowBand(page.ReportTitle);
- }
- }
-
- // show column header
- columnStartY = CurY;
- ShowBand(page.ColumnHeader);
-
- // calculate CurX before starting column event depending on Right to Left or Left to Right layout
- if (Config.RightToLeft)
- {
- CurX = page.Columns.Positions[page.Columns.Positions.Count - 1] * Units.Millimeters;
- }
- else
- {
- CurX = page.Columns.Positions[0] * Units.Millimeters;
- }
-
- // start column event
- OnStateChanged(page, EngineState.ColumnStarted);
- ShowProgress();
return previousPage;
}
diff --git a/FastReport.Base/Engine/ReportEngine.Subreports.Async.cs b/FastReport.Base/Engine/ReportEngine.Subreports.Async.cs
new file mode 100644
index 00000000..a551ecba
--- /dev/null
+++ b/FastReport.Base/Engine/ReportEngine.Subreports.Async.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using FastReport.Preview;
+
+namespace FastReport.Engine
+{
+ public partial class ReportEngine
+ {
+ #region Private Methods
+
+ private Task RenderSubreportAsync(SubreportObject subreport, CancellationToken cancellationToken)
+ {
+ if (subreport.ReportPage != null)
+ return RunBandsAsync(subreport.ReportPage.Bands, cancellationToken);
+ return Task.CompletedTask;
+ }
+
+ private async Task RenderOuterSubreportsAsync(BandBase parentBand, CancellationToken cancellationToken)
+ {
+ float saveCurY = CurY;
+ float saveOriginX = originX;
+ int saveCurPage = CurPage;
+
+ float maxY = 0;
+ int maxPage = CurPage;
+ bool hasSubreports = false;
+
+ try
+ {
+ for (int i = 0; i < parentBand.Objects.Count; i++)
+ {
+ SubreportObject subreport = parentBand.Objects[i] as SubreportObject;
+
+ // Apply visible expression if needed.
+ if (subreport != null && !String.IsNullOrEmpty(subreport.VisibleExpression))
+ {
+ subreport.Visible = subreport.CalcVisibleExpression(subreport.VisibleExpression);
+ }
+
+ if (subreport != null && subreport.Visible && !subreport.PrintOnParent)
+ {
+ hasSubreports = true;
+ // restore start position
+ CurPage = saveCurPage;
+ CurY = saveCurY - subreport.Height;
+ originX = saveOriginX + subreport.Left;
+ // do not upload generated pages to the file cache
+ PreparedPages.CanUploadToCache = false;
+
+ await RenderSubreportAsync(subreport, cancellationToken);
+
+ // find maxY. We will continue from maxY when all subreports finished.
+ if (CurPage == maxPage)
+ {
+ if (CurY > maxY)
+ maxY = CurY;
+ }
+ else if (CurPage > maxPage)
+ {
+ maxPage = CurPage;
+ maxY = CurY;
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (hasSubreports)
+ {
+ CurPage = maxPage;
+ CurY = maxY;
+ }
+ originX = saveOriginX;
+ PreparedPages.CanUploadToCache = true;
+ }
+ }
+
+ #endregion Private Methods
+ }
+}
diff --git a/FastReport.Base/Export/ExportUtils.cs b/FastReport.Base/Export/ExportUtils.cs
index 9d606340..4084a45b 100644
--- a/FastReport.Base/Export/ExportUtils.cs
+++ b/FastReport.Base/Export/ExportUtils.cs
@@ -223,25 +223,25 @@ internal static string GetExcelFormatSpecifier(FormatBase format, bool useLocale
switch (f.NumberNegativePattern)
{
case 0: negative_pattern = "(" + fm_str + ")"; break; // (n)
- case 1: negative_pattern = "-" + fm_str; break; // -n
+ // case 1: negative_pattern = "-" + fm_str; break; // -n
case 2: negative_pattern = "- " + fm_str; break; // - n
case 3: negative_pattern = fm_str + "-"; break; // n-
case 4: negative_pattern = fm_str + " -"; break; // n -
}
-
- return positive_pattern + ";" + negative_pattern;
+ string semicolon = !String.IsNullOrEmpty(negative_pattern) ? ";" : "";
+ return positive_pattern + semicolon + negative_pattern;
}
else if (format is DateFormat)
{
- string parentalCase = CultureInfo.CurrentCulture.TwoLetterISOLanguageName == "ru" ? "[$-FC19]" : "";
+ string parentalCase = CultureInfo.CurrentCulture.TwoLetterISOLanguageName == "ru" ? "[$-419]" : "";
switch ((format as DateFormat).Format)
{
case "d": return DateTimeFormatInfo.CurrentInfo.ShortDatePattern + ";@";
- case "D": return "[$-F800]" + DateTimeFormatInfo.CurrentInfo.LongDatePattern.Replace("tt", "AM/PM") + ";@";
+ case "D": return "[$-F800]" + "dddd, mmmm dd, yyyy";
case "f": return parentalCase + (DateTimeFormatInfo.CurrentInfo.LongDatePattern + " " + DateTimeFormatInfo.CurrentInfo.ShortTimePattern).Replace("tt", "AM/PM") + ";@";
case "F": return parentalCase + DateTimeFormatInfo.CurrentInfo.FullDateTimePattern.Replace("tt", "AM/PM") + ";@";
- case "MMMM yyyy": return (format as DateFormat).Format + ";@";
- default: return parentalCase + (format as DateFormat).Format.Replace("tt", "AM/PM") + ";@";
+ case "MMMM yyyy": return parentalCase + DateTimeFormatInfo.CurrentInfo.YearMonthPattern + ";@";
+ default: return (format as DateFormat).Format.Replace("tt", "AM/PM") + ";@";
}
}
else if (format is PercentFormat)
diff --git a/FastReport.Base/Export/Html/HTMLExportDraw.cs b/FastReport.Base/Export/Html/HTMLExportDraw.cs
index d76fd0a9..14118717 100644
--- a/FastReport.Base/Export/Html/HTMLExportDraw.cs
+++ b/FastReport.Base/Export/Html/HTMLExportDraw.cs
@@ -196,6 +196,7 @@ private void PrintPageStyle(FastString sb)
{
if (singlePage && pageBreaks)
{
+ string paperProps = "size: portrait; ";
sb.AppendLine("");
}
}
diff --git a/FastReport.Base/Export/Html/HTMLExportLayers.cs b/FastReport.Base/Export/Html/HTMLExportLayers.cs
index 3bd64cc2..9af4906a 100644
--- a/FastReport.Base/Export/Html/HTMLExportLayers.cs
+++ b/FastReport.Base/Export/Html/HTMLExportLayers.cs
@@ -184,7 +184,7 @@ private FastString GetSpanText(TextObjectBase obj, FastString text,
float ParagraphOffset)
{
FastString style = new FastString();
- style.Append("display:block;border:0;white-space: pre-wrap;width:").Append(Px(width * Zoom));
+ style.Append("display:block;border:0;width:").Append(Px(width * Zoom));
if (ParagraphOffset != 0)
style.Append("text-indent:").Append(Px(ParagraphOffset * Zoom));
if (obj.Padding.Left != 0)
diff --git a/FastReport.Base/Functions/NumToWordsBase.cs b/FastReport.Base/Functions/NumToWordsBase.cs
index c0a3a00a..c327bd7f 100644
--- a/FastReport.Base/Functions/NumToWordsBase.cs
+++ b/FastReport.Base/Functions/NumToWordsBase.cs
@@ -8,7 +8,7 @@ namespace FastReport.Functions
internal abstract class NumToWordsBase
{
#region Private Methods
- private string Str(decimal value, WordInfo senior, WordInfo junior)
+ private string Str(decimal value, WordInfo senior, WordInfo junior, bool dicimalPartToWord)
{
bool minus = false;
if (value < 0)
@@ -34,8 +34,20 @@ private string Str(decimal value, WordInfo senior, WordInfo junior)
if (junior != null)
{
- r.Append(GetDecimalSeparator() + remainder.ToString("00 "));
- r.Append(Case(remainder, junior));
+ string decimalPart;
+
+ if (dicimalPartToWord)
+ {
+ decimalPart = Str(remainder, junior, null, false).ToLower();
+ }
+ else
+ {
+ decimalPart = remainder.ToString("00 ");
+ }
+
+ r.Append(GetDecimalSeparator() + decimalPart);
+ if (!dicimalPartToWord)
+ r.Append(Case(remainder, junior));
}
r[0] = char.ToUpper(r[0]);
@@ -160,12 +172,12 @@ protected virtual string Case(long value, WordInfo info)
#endregion
#region Public Methods
- public string ConvertCurrency(decimal value, string currencyName)
+ public string ConvertCurrency(decimal value, string currencyName, bool decimalPartToWord)
{
try
{
CurrencyInfo currency = GetCurrency(currencyName);
- return Str(value, currency.senior, currency.junior);
+ return Str(value, currency.senior, currency.junior, decimalPartToWord);
}
catch (KeyNotFoundException e)
{
@@ -181,18 +193,18 @@ public string ConvertCurrency(decimal value, string currencyName)
}
}
- public string ConvertNumber(decimal value, bool male, string one, string two, string many)
+ public string ConvertNumber(decimal value, bool male, string one, string two, string many, bool decimalPartToWord)
{
- return Str(value, new WordInfo(male, one, two, many), null);
+ return Str(value, new WordInfo(male, one, two, many), null, decimalPartToWord);
}
public string ConvertNumber(decimal value, bool male,
string seniorOne, string seniorTwo, string seniorMany,
- string juniorOne, string juniorTwo, string juniorMany)
+ string juniorOne, string juniorTwo, string juniorMany, bool decimalPartToWord)
{
return Str(value,
new WordInfo(male, seniorOne, seniorTwo, seniorMany),
- new WordInfo(male, juniorOne, juniorTwo, juniorMany));
+ new WordInfo(male, juniorOne, juniorTwo, juniorMany), decimalPartToWord);
}
#endregion
}
diff --git a/FastReport.Base/Functions/StdFunctions.cs b/FastReport.Base/Functions/StdFunctions.cs
index 095ceee1..8ef3735c 100644
--- a/FastReport.Base/Functions/StdFunctions.cs
+++ b/FastReport.Base/Functions/StdFunctions.cs
@@ -713,6 +713,30 @@ public static string ToWords(object value)
return ToWords(value, "USD");
}
+ ///
+ /// Converts a currency value to an english (US) string representation of that value.
+ ///
+ /// The currency value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWords(object value, bool decimalPartToWord)
+ {
+ return ToWords(value, "USD", decimalPartToWord);
+ }
+
+ ///
+ /// Converts a currency value to an english (US) string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWords(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsEn().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
+ }
+
///
/// Converts a currency value to an english (US) string representation of that value,
/// using the specified currency.
@@ -722,7 +746,20 @@ public static string ToWords(object value)
/// The string representation of the specified value.
public static string ToWords(object value, string currencyName)
{
- return new NumToWordsEn().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsEn().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a numeric value to an english (US) string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "page".
+ /// The name in plural form, for example "pages".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWords(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsEn().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -734,7 +771,7 @@ public static string ToWords(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWords(object value, string one, string many)
{
- return new NumToWordsEn().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsEn().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
}
///
@@ -747,6 +784,17 @@ public static string ToWordsEnGb(object value)
return ToWordsEnGb(value, "GBP");
}
+ ///
+ /// Converts a currency value to an english (GB) string representation of that value.
+ ///
+ /// The currency value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsEnGb(object value, bool decimalPartToWord)
+ {
+ return ToWordsEnGb(value, "GBP", decimalPartToWord);
+ }
+
///
/// Converts a currency value to an english (GB) string representation of that value,
/// using the specified currency.
@@ -756,7 +804,20 @@ public static string ToWordsEnGb(object value)
/// The string representation of the specified value.
public static string ToWordsEnGb(object value, string currencyName)
{
- return new NumToWordsEnGb().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsEnGb().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a currency value to an english (GB) string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsEnGb(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsEnGb().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -768,7 +829,20 @@ public static string ToWordsEnGb(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsEnGb(object value, string one, string many)
{
- return new NumToWordsEnGb().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsEnGb().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to an english (GB) string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "page".
+ /// The name in plural form, for example "pages".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsEnGb(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsEnGb().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -781,6 +855,17 @@ public static string ToWordsEs(object value)
return ToWordsEs(value, "EUR");
}
+ ///
+ /// Converts a currency value to a spanish string representation of that value.
+ ///
+ /// The currency value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsEs(object value, bool decimalPartToWord)
+ {
+ return ToWordsEs(value, "EUR", decimalPartToWord);
+ }
+
///
/// Converts a currency value to a spanish string representation of that value,
/// using the specified currency.
@@ -790,7 +875,21 @@ public static string ToWordsEs(object value)
/// The string representation of the specified value.
public static string ToWordsEs(object value, string currencyName)
{
- return new NumToWordsEs().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsEs().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+
+ ///
+ /// Converts a currency value to a spanish string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsEs(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsEs().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -802,7 +901,20 @@ public static string ToWordsEs(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsEs(object value, string one, string many)
{
- return new NumToWordsEs().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsEs().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a spanish string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "page".
+ /// The name in plural form, for example "pages".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsEs(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsEs().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -815,6 +927,17 @@ public static string ToWordsRu(object value)
return ToWordsRu(value, "RUR");
}
+ ///
+ /// Converts a currency value to a russian string representation of that value.
+ ///
+ /// The currency value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsRu(object value, bool decimalPartToWord)
+ {
+ return ToWordsRu(value, "RUR", decimalPartToWord);
+ }
+
///
/// Converts a currency value to a russian string representation of that value,
/// using the specified currency.
@@ -824,7 +947,20 @@ public static string ToWordsRu(object value)
/// The string representation of the specified value.
public static string ToWordsRu(object value, string currencyName)
{
- return new NumToWordsRu().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsRu().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a currency value to a russian string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsRu(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsRu().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -838,7 +974,22 @@ public static string ToWordsRu(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsRu(object value, bool male, string one, string two, string many)
{
- return new NumToWordsRu().ConvertNumber(Convert.ToDecimal(value), male, one, two, many);
+ return new NumToWordsRu().ConvertNumber(Convert.ToDecimal(value), male, one, two, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a russian string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// True if the name is of male gender.
+ /// The name in singular form, for example "страница".
+ /// The name in plural form, for example "страницы".
+ /// The name in plural form, for example "страниц".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsRu(object value, bool male, string one, string two, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsRu().ConvertNumber(Convert.ToDecimal(value), male, one, two, many, decimalPartToWord);
}
///
@@ -851,6 +1002,17 @@ public static string ToWordsDe(object value)
return ToWordsDe(value, "EUR");
}
+ ///
+ /// Converts a currency value to a german string representation of that value.
+ ///
+ /// The currency value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsDe(object value, bool decimalPartToWord)
+ {
+ return ToWordsDe(value, "EUR", decimalPartToWord);
+ }
+
///
/// Converts a currency value to a german string representation of that value,
/// using the specified currency.
@@ -860,7 +1022,20 @@ public static string ToWordsDe(object value)
/// The string representation of the specified value.
public static string ToWordsDe(object value, string currencyName)
{
- return new NumToWordsDe().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsDe().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a currency value to a german string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsDe(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsDe().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -872,7 +1047,20 @@ public static string ToWordsDe(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsDe(object value, string one, string many)
{
- return new NumToWordsDe().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsDe().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a german string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "page".
+ /// The name in plural form, for example "pages".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsDe(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsDe().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -885,6 +1073,17 @@ public static string ToWordsFr(object value)
return ToWordsFr(value, "EUR");
}
+ ///
+ /// Converts a currency value to a french string representation of that value.
+ ///
+ /// The currency value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsFr(object value, bool decimalPartToWord)
+ {
+ return ToWordsFr(value, "EUR", decimalPartToWord);
+ }
+
///
/// Converts a currency value to a french string representation of that value,
/// using the specified currency.
@@ -894,7 +1093,20 @@ public static string ToWordsFr(object value)
/// The string representation of the specified value.
public static string ToWordsFr(object value, string currencyName)
{
- return new NumToWordsFr().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsFr().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a currency value to a french string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsFr(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsFr().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -906,7 +1118,20 @@ public static string ToWordsFr(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsFr(object value, string one, string many)
{
- return new NumToWordsFr().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsFr().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a french string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "page".
+ /// The name in plural form, for example "pages".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsFr(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsFr().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -919,6 +1144,17 @@ public static string ToWordsNl(object value)
return ToWordsNl(value, "EUR");
}
+ ///
+ /// Converts a currency value to a dutch string representation of that value.
+ ///
+ /// The currency value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsNl(object value, bool decimalPartToWord)
+ {
+ return ToWordsNl(value, "EUR", decimalPartToWord);
+ }
+
///
/// Converts a currency value to a dutch string representation of that value,
/// using the specified currency.
@@ -928,7 +1164,20 @@ public static string ToWordsNl(object value)
/// The string representation of the specified value.
public static string ToWordsNl(object value, string currencyName)
{
- return new NumToWordsNl().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsNl().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a currency value to a dutch string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsNl(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsNl().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -940,7 +1189,20 @@ public static string ToWordsNl(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsNl(object value, string one, string many)
{
- return new NumToWordsNl().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsNl().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a dutch string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "page".
+ /// The name in plural form, for example "pages".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsNl(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsNl().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -952,6 +1214,18 @@ public static string ToWordsIn(object value)
{
return ToWordsIn(value, "INR");
}
+
+ ///
+ /// Converts a numeric value to a indian numbering system string representation of that value.
+ ///
+ /// the currency value to convert
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsIn(object value, bool decimalPartToWord)
+ {
+ return ToWordsIn(value, "INR", decimalPartToWord);
+ }
+
///
/// Converts a numeric value to a indian numbering system string representation of that value.
///
@@ -960,7 +1234,19 @@ public static string ToWordsIn(object value)
///
public static string ToWordsIn(object value, string currencyName)
{
- return new NumToWordsIn().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsIn().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a numeric value to a indian numbering system string representation of that value.
+ ///
+ /// he numeric value to convert.
+ /// The 3-digit ISO name of the currency, for example "INR".
+ /// Flag indicating that decimal part should be converted to words.
+ ///
+ public static string ToWordsIn(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsIn().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -972,7 +1258,20 @@ public static string ToWordsIn(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsIn(object value, string one, string many)
{
- return new NumToWordsIn().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsIn().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a indian numbering system string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "page".
+ /// The name in plural form, for example "pages".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsIn(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsIn().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -985,6 +1284,16 @@ public static string ToWordsUkr(object value)
return ToWordsUkr(value, "UAH");
}
+ ///
+ /// Converts a numeric value to a ukrainian string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsUkr(object value, bool decimalPartToWord)
+ {
+ return ToWordsUkr(value, "UAH", decimalPartToWord);
+ }
///
/// Converts a currency value to a ukrainian string representation of that value,
@@ -995,7 +1304,20 @@ public static string ToWordsUkr(object value)
/// The string representation of the specified value.
public static string ToWordsUkr(object value, string currencyName)
{
- return new NumToWordsUkr().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsUkr().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a currency value to a ukrainian string representation of that value,
+ /// using the specified currency.
+ ///
+ /// The currency value to convert.
+ /// The 3-digit ISO name of the currency, for example "UAH".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsUkr(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsUkr().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -1009,9 +1331,23 @@ public static string ToWordsUkr(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsUkr(object value, bool male, string one, string two, string many)
{
- return new NumToWordsUkr().ConvertNumber(Convert.ToDecimal(value), male, one, two, many);
+ return new NumToWordsUkr().ConvertNumber(Convert.ToDecimal(value), male, one, two, many, false);
}
+ ///
+ /// Converts a numeric value to a ukrainian string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// True if the name is of male gender.
+ /// The name in singular form, for example "сторінка".
+ /// The name in plural form, for example "сторінки".
+ /// The name in plural form, for example "сторінок".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsUkr(object value, bool male, string one, string two, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsUkr().ConvertNumber(Convert.ToDecimal(value), male, one, two, many, decimalPartToWord);
+ }
///
/// Converts a numeric value to a spanish string representation of that value.
@@ -1023,6 +1359,17 @@ public static string ToWordsSp(object value)
return ToWordsSp(value, "EUR");
}
+ ///
+ /// Converts a numeric value to a spanish string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsSp(object value, bool decimalPartToWord)
+ {
+ return ToWordsSp(value, "EUR", decimalPartToWord);
+ }
+
///
/// Converts a numeric value to a spanish representation of that value.
///
@@ -1031,7 +1378,19 @@ public static string ToWordsSp(object value)
///
public static string ToWordsSp(object value, string currencyName)
{
- return new NumToWordsSp().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsSp().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a numeric value to a spanish representation of that value.
+ ///
+ /// he numeric value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ ///
+ public static string ToWordsSp(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsSp().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -1043,7 +1402,20 @@ public static string ToWordsSp(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsSp(object value, string one, string many)
{
- return new NumToWordsSp().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsSp().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a spanish string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "silla".
+ /// The name in plural form, for example "Sillas".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsSp(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsSp().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -1056,6 +1428,17 @@ public static string ToWordsPersian(object value)
return ToWordsPersian(value, "EUR");
}
+ ///
+ /// Converts a numeric value to a persian string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsPersian(object value, bool decimalPartToWord)
+ {
+ return ToWordsPersian(value, "EUR", decimalPartToWord);
+ }
+
///
/// Converts a numeric value to a persian representation of that value.
///
@@ -1064,7 +1447,19 @@ public static string ToWordsPersian(object value)
///
public static string ToWordsPersian(object value, string currencyName)
{
- return new NumToWordsPersian().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsPersian().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a numeric value to a persian representation of that value.
+ ///
+ /// he numeric value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ ///
+ public static string ToWordsPersian(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsPersian().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -1076,7 +1471,20 @@ public static string ToWordsPersian(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsPersian(object value, string one, string many)
{
- return new NumToWordsPersian().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsPersian().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a persian string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "silla".
+ /// The name in plural form, for example "Sillas".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsPersian(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsPersian().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -1089,6 +1497,17 @@ public static string ToWordsPl(object value)
return ToWordsPl(value, "PLN");
}
+ ///
+ /// Converts a numeric value to a polish string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsPl(object value, bool decimalPartToWord)
+ {
+ return ToWordsPl(value, "PLN", decimalPartToWord);
+ }
+
///
/// Converts a numeric value to a polish representation of that value.
///
@@ -1097,7 +1516,19 @@ public static string ToWordsPl(object value)
///
public static string ToWordsPl(object value, string currencyName)
{
- return new NumToWordsPl().ConvertCurrency(Convert.ToDecimal(value), currencyName);
+ return new NumToWordsPl().ConvertCurrency(Convert.ToDecimal(value), currencyName, false);
+ }
+
+ ///
+ /// Converts a numeric value to a polish representation of that value.
+ ///
+ /// he numeric value to convert.
+ /// The 3-digit ISO name of the currency, for example "EUR".
+ /// Flag indicating that decimal part should be converted to words.
+ ///
+ public static string ToWordsPl(object value, string currencyName, bool decimalPartToWord)
+ {
+ return new NumToWordsPl().ConvertCurrency(Convert.ToDecimal(value), currencyName, decimalPartToWord);
}
///
@@ -1109,7 +1540,20 @@ public static string ToWordsPl(object value, string currencyName)
/// The string representation of the specified value.
public static string ToWordsPl(object value, string one, string many)
{
- return new NumToWordsPl().ConvertNumber(Convert.ToDecimal(value), true, one, many, many);
+ return new NumToWordsPl().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, false);
+ }
+
+ ///
+ /// Converts a numeric value to a polish string representation of that value.
+ ///
+ /// The numeric value to convert.
+ /// The name in singular form, for example "silla".
+ /// The name in plural form, for example "Sillas".
+ /// Flag indicating that decimal part should be converted to words.
+ /// The string representation of the specified value.
+ public static string ToWordsPl(object value, string one, string many, bool decimalPartToWord)
+ {
+ return new NumToWordsPl().ConvertNumber(Convert.ToDecimal(value), true, one, many, many, decimalPartToWord);
}
///
@@ -1363,45 +1807,81 @@ internal static void Register()
RegisteredObjects.InternalAddFunction(stdConv.GetMethod("ToSingle", new Type[] { typeof(object) }), "Conversion");
RegisteredObjects.InternalAddFunction(stdConv.GetMethod("ToString", new Type[] { typeof(object) }), "Conversion");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWords", new Type[] { typeof(object) }), "Conversion,ToWords");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWords", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWords");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWords", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWords");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWords", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWords");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWords", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWords");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWords", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWords");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsIn", new Type[] { typeof(object) }), "Conversion,ToWordsIn");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsIn", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsIn");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsIn", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsIn");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsIn", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsIn");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsIn", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsIn");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsIn", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsIn");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsDe", new Type[] { typeof(object) }), "Conversion,ToWordsDe");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsDe", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsDe");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsDe", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsDe");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsDe", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsDe");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsDe", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsDe");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsDe", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsDe");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEnGb", new Type[] { typeof(object) }), "Conversion,ToWordsEnGb");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEnGb", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsEnGb");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEnGb", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsEnGb");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEnGb", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsEnGb");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEnGb", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsEnGb");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEnGb", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsEnGb");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEs", new Type[] { typeof(object) }), "Conversion,ToWordsEs");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEs", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsEs");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEs", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsEs");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEs", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsEs");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEs", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsEs");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsEs", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsEs");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsFr", new Type[] { typeof(object) }), "Conversion,ToWordsFr");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsFr", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsFr");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsFr", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsFr");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsFr", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsFr");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsFr", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsFr");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsFr", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsFr");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsNl", new Type[] { typeof(object) }), "Conversion,ToWordsNl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsNl", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsNl");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsNl", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsNl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsNl", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsNl");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsNl", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsNl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsNl", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsNl");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsRu", new Type[] { typeof(object) }), "Conversion,ToWordsRu");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsRu", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsRu");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsRu", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsRu");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsRu", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsRu");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsRu", new Type[] { typeof(object), typeof(bool), typeof(string), typeof(string), typeof(string) }), "Conversion,ToWordsRu");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsRu", new Type[] { typeof(object), typeof(bool), typeof(string), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsRu");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsUkr", new Type[] { typeof(object) }), "Conversion,ToWordsUkr");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsUkr", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsUkr");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsUkr", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsUkr");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsUkr", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsUkr");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsUkr", new Type[] { typeof(object), typeof(bool), typeof(string), typeof(string), typeof(string) }), "Conversion,ToWordsUkr");
- RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsSp", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsSp");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsUkr", new Type[] { typeof(object), typeof(bool), typeof(string), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsUkr");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsSp", new Type[] { typeof(object) }), "Conversion,ToWordsSp");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsSp", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsSp");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsSp", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsSp");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsSp", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsSp");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsSp", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsSp");
- RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPersian", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsPersian");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsSp", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsSp");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPersian", new Type[] { typeof(object) }), "Conversion,ToWordsPersian");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPersian", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsPersian");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPersian", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsPersian");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPersian", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsPersian");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPersian", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsPersian");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPersian", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsPersian");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object) }), "Conversion,ToWordsPl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToWordsPl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsPl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string), typeof(bool) }), "Conversion,ToWordsPl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsPl");
+ RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string), typeof(string), typeof(bool) }), "Conversion,ToWordsPl");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToLetters", new Type[] { typeof(object) }), "Conversion,ToLetters");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToLetters", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToLetters");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToLettersRu", new Type[] { typeof(object) }), "Conversion,ToLettersRu");
RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToLettersRu", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToLettersRu");
- RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsPl");
- RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object) }), "Conversion,ToWordsPl");
- RegisteredObjects.InternalAddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsPl");
#endregion
#region Program Flow
diff --git a/FastReport.Base/Gauge/GaugeObject.Async.cs b/FastReport.Base/Gauge/GaugeObject.Async.cs
new file mode 100644
index 00000000..3120ecd2
--- /dev/null
+++ b/FastReport.Base/Gauge/GaugeObject.Async.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport.Gauge
+{
+ public partial class GaugeObject
+ {
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+
+ #endregion // Report Engine
+ }
+}
diff --git a/FastReport.Base/Gauge/GaugeObject.cs b/FastReport.Base/Gauge/GaugeObject.cs
index c1047d52..a53d8993 100644
--- a/FastReport.Base/Gauge/GaugeObject.cs
+++ b/FastReport.Base/Gauge/GaugeObject.cs
@@ -221,7 +221,11 @@ public override string[] GetExpressions()
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+ private void GetDataShared()
+ {
if (!String.IsNullOrEmpty(Expression))
{
object val = Report.Calc(Expression);
diff --git a/FastReport.Base/HtmlObject.Async.cs b/FastReport.Base/HtmlObject.Async.cs
new file mode 100644
index 00000000..cfd256db
--- /dev/null
+++ b/FastReport.Base/HtmlObject.Async.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport
+{
+ public partial class HtmlObject
+ {
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/HtmlObject.cs b/FastReport.Base/HtmlObject.cs
index 1b4faea9..f7f830f1 100644
--- a/FastReport.Base/HtmlObject.cs
+++ b/FastReport.Base/HtmlObject.cs
@@ -205,7 +205,11 @@ public override float CalcHeight()
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+ private void GetDataShared()
+ {
// process expressions
if (AllowExpressions)
{
diff --git a/FastReport.Base/IContainDataSource.cs b/FastReport.Base/IContainDataSource.cs
new file mode 100644
index 00000000..9cd50f3a
--- /dev/null
+++ b/FastReport.Base/IContainDataSource.cs
@@ -0,0 +1,9 @@
+using FastReport.Data;
+
+namespace FastReport
+{
+ internal interface IContainDataSource
+ {
+ void UpdateDataSourceRef(DataSourceBase newRefDatasource);
+ }
+}
diff --git a/FastReport.Base/Matrix/MatrixObject.Async.cs b/FastReport.Base/Matrix/MatrixObject.Async.cs
new file mode 100644
index 00000000..c5594908
--- /dev/null
+++ b/FastReport.Base/Matrix/MatrixObject.Async.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport.Matrix
+{
+ public partial class MatrixObject
+ {
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/Matrix/MatrixObject.cs b/FastReport.Base/Matrix/MatrixObject.cs
index e56c3687..dbe59199 100644
--- a/FastReport.Base/Matrix/MatrixObject.cs
+++ b/FastReport.Base/Matrix/MatrixObject.cs
@@ -91,7 +91,7 @@ public enum MatrixEvenStylePriority
/// matrix.Data.Rows[0].TemplateTotalCell.Text = "Grand Total";
///
///
- public partial class MatrixObject : TableBase
+ public partial class MatrixObject : TableBase, IContainDataSource
{
#region Fields
private bool autoSize;
@@ -616,6 +616,14 @@ private void dataBand_BeforePrint(object sender, EventArgs e)
if (match is bool && (bool)match == true)
Helper.AddDataRow();
}
+
+ void IContainDataSource.UpdateDataSourceRef(DataSourceBase newRefDatasource)
+ {
+ if (newRefDatasource != null && (newRefDatasource.Name == DataSource.Name || newRefDatasource == DataSource))
+ {
+ DataSource = newRefDatasource;
+ }
+ }
#endregion
#region Protected Methods
@@ -774,7 +782,11 @@ public override void SaveState()
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+ private void GetDataShared()
+ {
if (!IsOnFooter)
{
Helper.StartPrint();
diff --git a/FastReport.Base/PictureObject.Async.cs b/FastReport.Base/PictureObject.Async.cs
new file mode 100644
index 00000000..f1fc8eee
--- /dev/null
+++ b/FastReport.Base/PictureObject.Async.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Drawing;
+using FastReport.Utils;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport
+{
+ public partial class PictureObject
+ {
+ #region Public Methods
+
+ public override async Task LoadImageAsync(CancellationToken cancellationToken)
+ {
+ if (!String.IsNullOrEmpty(ImageLocation))
+ {
+ try
+ {
+ Uri uri = CalculateUri();
+ byte[] bytes;
+ if (uri.IsFile)
+ bytes = await ImageHelper.LoadAsync(uri.LocalPath, cancellationToken);
+ else
+ bytes = await ImageHelper.LoadURLAsync(uri, cancellationToken);
+ SetImageData(bytes);
+ }
+ catch
+ {
+ Image = null;
+ }
+
+ ShouldDisposeImage = true;
+ }
+ }
+
+#endregion
+
+ #region Report Engine
+
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+
+ if (!String.IsNullOrEmpty(DataColumn))
+ {
+ // reset the image
+ Image = null;
+ imageData = null;
+
+ object data = Report.GetColumnValueNullable(DataColumn);
+ if (data is byte[])
+ {
+ SetImageData((byte[])data);
+ }
+ else if (data is Image)
+ {
+ Image = data as Image;
+ }
+ else if (data is string dataStr)
+ {
+ await SetImageLocationAsync(dataStr, true, cancellationToken);
+ }
+ }
+ else
+ {
+ // no other data received
+ await UpdateImageLocationAsync(cancellationToken);
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/PictureObject.cs b/FastReport.Base/PictureObject.cs
index 5faf3517..31d500e4 100644
--- a/FastReport.Base/PictureObject.cs
+++ b/FastReport.Base/PictureObject.cs
@@ -666,7 +666,7 @@ public void EstablishImageForm(GraphicsPath path, float drawLeft, float drawTop,
break;
}
}
- #endregion
+#endregion
#region Report Engine
@@ -691,6 +691,7 @@ public override void FinalizeComponent()
public override void GetData()
{
base.GetData();
+
if (!String.IsNullOrEmpty(DataColumn))
{
// reset the image
@@ -701,16 +702,23 @@ public override void GetData()
if (data is byte[])
{
SetImageData((byte[])data);
+ return;
}
else if (data is Image)
{
Image = data as Image;
+ return;
}
- else if (data is string)
+ else if (data is string dataStr)
{
- ImageLocation = data.ToString();
+ SetImageLocation(dataStr, true);
}
}
+ else
+ {
+ // no other data received
+ UpdateImageLocation();
+ }
}
///
diff --git a/FastReport.Base/PictureObjectBase.Async.cs b/FastReport.Base/PictureObjectBase.Async.cs
new file mode 100644
index 00000000..f9c3f021
--- /dev/null
+++ b/FastReport.Base/PictureObjectBase.Async.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using FastReport.Utils;
+
+namespace FastReport
+{
+ public abstract partial class PictureObjectBase
+ {
+ internal async Task SetImageLocationAsync(string value, bool forceUpdate, CancellationToken cancellationToken)
+ {
+ if (!String.IsNullOrEmpty(Config.ReportSettings.ImageLocationRoot))
+ imageLocation = value.Replace(Config.ReportSettings.ImageLocationRoot, "");
+ else
+ imageLocation = value;
+
+ if (forceUpdate)
+ await UpdateImageLocationAsync(cancellationToken);
+ }
+
+ internal async Task UpdateImageLocationAsync(CancellationToken cancellationToken)
+ {
+ await LoadImageAsync(cancellationToken);
+ ResetImageIndex();
+ }
+
+ #region Public Methods
+
+ ///
+ /// Loads image asynchronously
+ ///
+ ///
+ /// You mustn't call this method when override it in nested class because it will call synchronous implementation
+ ///
+ public virtual Task LoadImageAsync(CancellationToken cancellationToken)
+ {
+ // fallback if this method wasn't overridden
+ LoadImage();
+ return Task.CompletedTask;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/PictureObjectBase.cs b/FastReport.Base/PictureObjectBase.cs
index a9a96294..3c26c021 100644
--- a/FastReport.Base/PictureObjectBase.cs
+++ b/FastReport.Base/PictureObjectBase.cs
@@ -158,12 +158,7 @@ public string ImageLocation
get { return imageLocation; }
set
{
- if (!String.IsNullOrEmpty(Config.ReportSettings.ImageLocationRoot))
- imageLocation = value.Replace(Config.ReportSettings.ImageLocationRoot, "");
- else
- imageLocation = value;
- LoadImage();
- ResetImageIndex();
+ SetImageLocation(value, true); // or IsDesigning
}
}
@@ -898,5 +893,22 @@ public override string[] GetExpressions()
return expressions.ToArray();
}
+
+ internal void SetImageLocation(string value, bool forceUpdate)
+ {
+ if (!String.IsNullOrEmpty(Config.ReportSettings.ImageLocationRoot))
+ imageLocation = value.Replace(Config.ReportSettings.ImageLocationRoot, "");
+ else
+ imageLocation = value;
+
+ if (forceUpdate)
+ UpdateImageLocation();
+ }
+
+ internal void UpdateImageLocation()
+ {
+ LoadImage();
+ ResetImageIndex();
+ }
}
}
\ No newline at end of file
diff --git a/FastReport.Base/RFIDLabel.Async.cs b/FastReport.Base/RFIDLabel.Async.cs
new file mode 100644
index 00000000..4374925b
--- /dev/null
+++ b/FastReport.Base/RFIDLabel.Async.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport
+{
+ public partial class RFIDLabel
+ {
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+ }
+}
diff --git a/FastReport.Base/RFIDLabel.cs b/FastReport.Base/RFIDLabel.cs
index 1ddf32ee..42943a8d 100644
--- a/FastReport.Base/RFIDLabel.cs
+++ b/FastReport.Base/RFIDLabel.cs
@@ -401,6 +401,11 @@ public bool AdaptiveAntenna
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
if (TIDBank.DataColumn.Contains("[") && TIDBank.DataColumn.Contains("]"))
TIDBank.Data = Report.Calc(TIDBank.DataColumn).ToString();
diff --git a/FastReport.Base/Report.Async.cs b/FastReport.Base/Report.Async.cs
new file mode 100644
index 00000000..7c26a79e
--- /dev/null
+++ b/FastReport.Base/Report.Async.cs
@@ -0,0 +1,117 @@
+using FastReport.Code;
+using FastReport.Engine;
+using FastReport.Utils;
+
+using System.ComponentModel;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport
+{
+ public partial class Report
+ {
+
+ #region Script related
+
+ 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();
+ }
+ }
+
+ ///
+ /// Prepares the report asynchronously.
+ ///
+ /// Cancellation token
+ /// true if report was prepared successfully.
+ public Task PrepareAsync(CancellationToken token = default)
+ {
+ return PrepareAsync(false, token);
+ }
+
+ public Task PrepareAsync(bool append, CancellationToken token = default)
+ {
+ return PrepareAsync(append, true, token);
+ }
+
+ public async Task PrepareAsync(bool append, bool resetDataState, 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 await Engine.RunAsync(true, append, resetDataState, token);
+ }
+ finally
+ {
+ if (!Config.WebMode)
+ StopPerformanceCounter();
+ }
+ }
+ finally
+ {
+ SetRunning(false);
+ }
+ }
+
+
+ ///
+ /// For internal use only.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public async Task PreparePhase1Async(CancellationToken cancellationToken)
+ {
+ 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);
+ await CompileAsync(cancellationToken);
+ Engine.RunPhase1(true, webDialog);
+ }
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/Report.cs b/FastReport.Base/Report.cs
index 4a4ef574..41dd8499 100644
--- a/FastReport.Base/Report.cs
+++ b/FastReport.Base/Report.cs
@@ -18,8 +18,6 @@
using System.Linq.Expressions;
using System.Security;
using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
using System.Windows.Forms;
namespace FastReport
@@ -909,6 +907,21 @@ internal CodeHelperBase CodeHelper
get { return codeHelper; }
}
+ internal bool HasPageLinks
+ {
+ get
+ {
+ foreach(var page in Pages)
+ {
+ if(page is ReportPage reportPage && reportPage.LinkToPage.IsInherit)
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+
public IGraphics MeasureGraphics
{
get
@@ -1306,36 +1319,6 @@ internal void Compile()
}
}
-#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.
@@ -1643,6 +1626,10 @@ public object GetParameterValue(string complexName)
{
return (double)(int)par.Value;
}
+ if (par.Value.GetType() != par.DataType)
+ {
+ return ConvertToColumnDataType(par.Value, par.DataType, Report.ConvertNulls);
+ }
return par.Value;
}
return null;
@@ -2000,6 +1987,42 @@ public void Save(Stream stream)
}
}
+ ///
+ /// Saves the report to a stream.
+ ///
+ /// The stream to save to.
+ /// Enables saving linked pages to original files.
+ public void Save(Stream stream, bool savePageLinks)
+ {
+ using (FRWriter writer = new FRWriter())
+ {
+ writer.SaveExternalPages = savePageLinks;
+
+ 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.
///
@@ -2013,6 +2036,20 @@ public void Save(string fileName)
}
}
+ ///
+ /// Saves the report to a file.
+ ///
+ /// The name of the file to save to.
+ /// Enables saving linked pages to original files.
+ public void Save(string fileName, bool savePageLinks)
+ {
+ FileName = fileName;
+ using (FileStream f = new FileStream(fileName, FileMode.Create))
+ {
+ Save(f, savePageLinks);
+ }
+ }
+
///
/// Saves the report to a stream with randomized values in data sources.
///
@@ -2162,7 +2199,29 @@ public string SaveToString()
{
using (MemoryStream stream = new MemoryStream())
{
- Save(stream);
+ Save(stream, false);
+
+ if (Compressed || !String.IsNullOrEmpty(Password))
+ {
+ return Convert.ToBase64String(stream.ToArray());
+ }
+ else
+ {
+ return Encoding.UTF8.GetString(stream.ToArray());
+ }
+ }
+ }
+
+ ///
+ /// Saves the report to a string.
+ ///
+ /// The string that contains a stream.
+ /// Enables saving linked pages to original files.
+ public string SaveToString(bool savePageLinks)
+ {
+ using (MemoryStream stream = new MemoryStream())
+ {
+ Save(stream, savePageLinks);
if (Compressed || !String.IsNullOrEmpty(Password))
{
@@ -2461,60 +2520,38 @@ public bool Prepare()
return Prepare(false);
}
-#if ASYNC
+
///
- /// Prepares the report asynchronously.
+ /// Prepares the report.
///
- /// 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)
+ /// Specifies whether the new report should be added to a
+ /// report that was prepared before.
+ /// true if report was prepared successfully.
+ ///
+ /// 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
- {
- await CompileAsync(token).ConfigureAwait(false);
- isParameterChanged = false;
- return Engine.Run(true, append, true);
- }
- finally
- {
- if (!Config.WebMode)
- StopPerformanceCounter();
- }
- }
- finally
- {
- SetRunning(false);
- }
+ return Prepare(append, true);
}
-#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.
+ /// Specifies whether the reset data state.
+ /// true if report was prepared successfully.
///
/// Use this method to merge prepared reports.
///
@@ -2528,7 +2565,7 @@ private async Task PrepareAsync(bool append, CancellationToken token = def
/// report.ShowPrepared();
///
///
- public bool Prepare(bool append)
+ public bool Prepare(bool append, bool resetDataState)
{
SetRunning(true);
try
@@ -2548,7 +2585,7 @@ public bool Prepare(bool append)
{
Compile();
isParameterChanged = false;
- return Engine.Run(true, append, true);
+ return Engine.Run(true, append, resetDataState);
}
finally
{
diff --git a/FastReport.Base/ReportComponentBase.Async.cs b/FastReport.Base/ReportComponentBase.Async.cs
new file mode 100644
index 00000000..055cf2eb
--- /dev/null
+++ b/FastReport.Base/ReportComponentBase.Async.cs
@@ -0,0 +1,14 @@
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FastReport
+{
+ public abstract partial class ReportComponentBase
+ {
+ public virtual Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ GetDataShared();
+ return Task.CompletedTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/ReportComponentBase.cs b/FastReport.Base/ReportComponentBase.cs
index 0564a123..be776524 100644
--- a/FastReport.Base/ReportComponentBase.cs
+++ b/FastReport.Base/ReportComponentBase.cs
@@ -1004,6 +1004,11 @@ public virtual float CalcHeight()
/// In this method you should get the data from a datasource that the object is connected to.
///
public virtual void GetData()
+ {
+ GetDataShared();
+ }
+
+ private void GetDataShared()
{
Hyperlink.Calculate();
diff --git a/FastReport.Base/ReportPage.cs b/FastReport.Base/ReportPage.cs
index ce0181e9..e3327757 100644
--- a/FastReport.Base/ReportPage.cs
+++ b/FastReport.Base/ReportPage.cs
@@ -5,6 +5,9 @@
using FastReport.Utils;
using System.Drawing.Design;
using System.Drawing.Printing;
+using System.IO;
+using System.Xml;
+using FastReport.Data;
namespace FastReport
{
@@ -47,6 +50,128 @@ namespace FastReport
///
public partial class ReportPage : PageBase, IParent
{
+ [TypeConverter(typeof(FastReport.TypeConverters.FRExpandableObjectConverter))]
+ public class PageLink
+ {
+ string reportPath;
+ string pageName;
+ bool saveNames;
+ bool isInherit;
+ ReportPage page;
+
+ ///
+ /// Get or set path to report file.
+ ///
+ public string ReportPath
+ {
+ get
+ {
+ return reportPath;
+ }
+ set
+ {
+ reportPath = value;
+ if (value != reportPath && !string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(pageName))
+ {
+ page.LoadExternalPage(this, page.Report, page.Name);
+ }
+ }
+ }
+
+ [Browsable(false)]
+ public bool IsInherit
+ {
+ get
+ {
+ return isInherit;
+ }
+ internal set
+ {
+ isInherit = value;
+ }
+ }
+
+
+ ///
+ /// Get or set name of linked page.
+ ///
+ public string PageName
+ {
+ get
+ {
+ return pageName;
+ }
+ set
+ {
+ pageName = value;
+ if (value != pageName && !string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(reportPath))
+ {
+ page.LoadExternalPage(this, page.Report, page.Name);
+ }
+ }
+ }
+
+
+ ///
+ /// Gets or sets a value indicating whether need save original name of objects.
+ ///
+ public bool SaveNames
+ {
+ get
+ {
+ return saveNames;
+ }
+ set
+ {
+ if (value != saveNames)
+ {
+ saveNames = value;
+ page.LoadExternalPage(this, page.Report, page.Name);
+ }
+ }
+ }
+
+ public void Deserialize(FRReader reader, string prefix)
+ {
+ reportPath = reader.ReadStr(prefix + ".ReportPath");
+ pageName = reader.ReadStr(prefix + ".PageName");
+ saveNames = reader.ReadBool(prefix + ".SaveName");
+ isInherit = reader.ReadBool(prefix + ".IsInherit");
+ }
+
+ ///
+ public void Serialize(FRWriter writer, string prefix, PageLink c)
+ {
+ if (ReportPath != c.ReportPath)
+ writer.WriteStr(prefix + ".ReportPath", ReportPath);
+ if (PageName != c.PageName)
+ writer.WriteStr(prefix + ".PageName", PageName);
+ if (SaveNames != c.SaveNames)
+ writer.WriteBool(prefix + ".SaveName", SaveNames);
+ if (IsInherit != c.IsInherit)
+ writer.WriteBool(prefix + ".IsInherit", IsInherit);
+ }
+
+ internal PageLink Clone()
+ {
+ var result = new PageLink(page);
+ result.reportPath = ReportPath;
+ result.pageName = PageName;
+ result.saveNames = SaveNames;
+ result.IsInherit = IsInherit;
+ return result;
+ }
+
+ public PageLink(ReportPage page)
+ {
+ saveNames = false;
+ reportPath = "";
+ pageName = "";
+ isInherit = false;
+ this.page = page;
+ }
+ }
+
#region Constants
private const float MAX_PAPER_SIZE_MM = 2000000000;
@@ -93,6 +218,7 @@ public partial class ReportPage : PageBase, IParent
private int otherPagesSource;
private int lastPageSource;
private Duplex duplex;
+ private PageLink pageLink;
private bool unlimitedHeight;
private bool printOnRollPaper;
@@ -784,6 +910,15 @@ internal bool IsManualBuild
{
get { return !String.IsNullOrEmpty(manualBuildEvent) || ManualBuild != null; }
}
+
+ ///
+ /// Get or set a link to the page.
+ ///
+ public PageLink LinkToPage
+ {
+ get { return pageLink; }
+ set { pageLink = value; }
+ }
#endregion
#region Private Methods
@@ -982,13 +1117,21 @@ public override void Assign(Base source)
UnlimitedWidth = src.UnlimitedWidth;
UnlimitedHeightValue = src.UnlimitedHeightValue;
UnlimitedWidthValue = src.UnlimitedWidthValue;
+ LinkToPage = src.LinkToPage.Clone();
}
///
public override void Serialize(FRWriter writer)
{
ReportPage c = writer.DiffObject as ReportPage;
+ bool saveChild = writer.SaveChildren;
+ if (!string.IsNullOrEmpty(this.LinkToPage.ReportPath) && writer.SaveExternalPages)
+ {
+ if (writer.SaveChildren && writer.SerializeTo == SerializeTo.Report)
+ writer.SaveChildren = false;
+ }
base.Serialize(writer);
+
if (ExportAlias != c.ExportAlias)
writer.WriteStr("ExportAlias", ExportAlias);
if (Landscape != c.Landscape)
@@ -1055,6 +1198,102 @@ public override void Serialize(FRWriter writer)
writer.WriteFloat("OtherPageSource", OtherPagesSource);
if (Duplex.ToString() != c.Duplex.ToString())
writer.WriteStr("Duplex", Duplex.ToString());
+ if (writer.SerializeTo != SerializeTo.SourcePages)
+ LinkToPage.Serialize(writer, nameof(LinkToPage), c.LinkToPage);
+
+ if (writer.SerializeTo == SerializeTo.Report && writer.SaveExternalPages && !string.IsNullOrEmpty(LinkToPage.PageName)
+ && !string.IsNullOrEmpty(LinkToPage.ReportPath) && File.Exists(LinkToPage.ReportPath))
+ {
+ using (Report report = new Report())
+ {
+ report.Load(LinkToPage.ReportPath);
+ foreach (PageBase item in report.Pages)
+ {
+ if (item is ReportPage page && item.Name == LinkToPage.PageName)
+ {
+ var temp = page.LinkToPage;
+ bool isAncestor = page.IsAncestor;
+ page.AssignAll(this, true, true);
+ page.SetAncestor(isAncestor);
+ page.SetReport(report);
+ page.Parent = report;
+ page.LinkToPage = temp;
+ if (!LinkToPage.SaveNames)
+ {
+ if (!string.IsNullOrEmpty(page.Alias))
+ page.Name = page.Alias;
+ foreach (Base obj in page.AllObjects)
+ {
+ if (!string.IsNullOrEmpty(obj.Alias))
+ {
+ obj.SetReport(report);
+ obj.SetName(obj.Alias);
+ }
+ }
+ }
+ break;
+ }
+ }
+ report.Save(LinkToPage.ReportPath);
+ }
+ }
+ writer.SaveChildren = saveChild;
+ }
+
+ ///
+ public override void Deserialize(FRReader reader)
+ {
+ LinkToPage.Deserialize(reader, nameof(LinkToPage));
+ var page = LinkToPage;
+
+ if ((reader.DeserializeFrom == SerializeTo.Report || page.IsInherit) && !string.IsNullOrEmpty(LinkToPage.PageName)
+ && !string.IsNullOrEmpty(page.ReportPath) && File.Exists(page.ReportPath))
+ LoadExternalPage(page, reader.Report, reader.ReadStr("Name"));
+
+ base.Deserialize(reader);
+ }
+
+ private void LoadExternalPage(PageLink page, Report parent, string pageName)
+ {
+ try
+ {
+ using (Report report = new Report())
+ {
+ report.Load(page.ReportPath);
+ page.IsInherit = report.IsAncestor;
+ foreach (PageBase item in report.Pages)
+ {
+ if (item is ReportPage && item.Name == page.PageName)
+ {
+ AssignAll(item, true, true);
+ SetAncestor(false);
+ LinkToPage = page;
+ if (!LinkToPage.SaveNames)
+ {
+ Alias = Name;
+ Name = pageName;
+ Parent = parent;
+ SetReport(parent);
+ foreach (Base obj in AllObjects)
+ {
+ obj.Alias = obj.Name;
+ obj.SetReport(parent);
+ obj.CreateUniqueName();
+ }
+ }
+ break;
+ }
+ else
+ {
+ Clear();
+ }
+ }
+ }
+ }
+ catch
+ {
+ Clear();
+ }
}
///
@@ -1271,6 +1510,7 @@ public ReportPage()
unlimitedHeight = false;
printOnRollPaper = false;
unlimitedWidth = false;
+ pageLink = new PageLink(this);
unlimitedHeightValue = MAX_PAPER_SIZE_MM * Units.Millimeters;
unlimitedWidthValue = MAX_PAPER_SIZE_MM * Units.Millimeters;
}
diff --git a/FastReport.Base/Table/TableCell.Async.cs b/FastReport.Base/Table/TableCell.Async.cs
new file mode 100644
index 00000000..fe1245a3
--- /dev/null
+++ b/FastReport.Base/Table/TableCell.Async.cs
@@ -0,0 +1,20 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Table
+{
+ public partial class TableCell
+ {
+
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+
+ #endregion
+ }
+}
diff --git a/FastReport.Base/Table/TableCell.cs b/FastReport.Base/Table/TableCell.cs
index 16593610..a744ad91 100644
--- a/FastReport.Base/Table/TableCell.cs
+++ b/FastReport.Base/Table/TableCell.cs
@@ -392,6 +392,11 @@ public override void RestoreState()
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
if (Table != null && Table.IsInsideSpan(this))
Text = "";
diff --git a/FastReport.Base/Table/TableObject.Async.cs b/FastReport.Base/Table/TableObject.Async.cs
new file mode 100644
index 00000000..3fdd923a
--- /dev/null
+++ b/FastReport.Base/Table/TableObject.Async.cs
@@ -0,0 +1,20 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Table
+{
+ public partial class TableObject
+ {
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+
+ GetDataShared();
+ }
+
+ #endregion
+ }
+}
diff --git a/FastReport.Base/Table/TableObject.cs b/FastReport.Base/Table/TableObject.cs
index 2fd922a8..9c6e9c55 100644
--- a/FastReport.Base/Table/TableObject.cs
+++ b/FastReport.Base/Table/TableObject.cs
@@ -349,6 +349,11 @@ public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
if (!IsManualBuild)
{
for (int y = 0; y < Rows.Count; y++)
diff --git a/FastReport.Base/TextObject.Async.cs b/FastReport.Base/TextObject.Async.cs
new file mode 100644
index 00000000..3a0d6a05
--- /dev/null
+++ b/FastReport.Base/TextObject.Async.cs
@@ -0,0 +1,21 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport
+{
+ public partial class TextObject
+ {
+
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+
+ GetDataShared();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/TextObject.cs b/FastReport.Base/TextObject.cs
index d1a243d4..47dde96e 100644
--- a/FastReport.Base/TextObject.cs
+++ b/FastReport.Base/TextObject.cs
@@ -1624,6 +1624,11 @@ public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
// process expressions
if (AllowExpressions)
{
diff --git a/FastReport.Base/Utils/FRReader.cs b/FastReport.Base/Utils/FRReader.cs
index cf2b4162..3c355ab8 100644
--- a/FastReport.Base/Utils/FRReader.cs
+++ b/FastReport.Base/Utils/FRReader.cs
@@ -258,7 +258,7 @@ public IFRSerializable Read()
curRoot = curItem;
GetProps();
- if (report != null && report.IsAncestor)
+ if (report != null && (report.IsAncestor || report.HasPageLinks))
result = report.FindObject(ReadStr("Name"));
if (result == null && curItem.Name != "inherited")
{
diff --git a/FastReport.Base/Utils/FRWriter.cs b/FastReport.Base/Utils/FRWriter.cs
index 211b39e4..33c774c8 100644
--- a/FastReport.Base/Utils/FRWriter.cs
+++ b/FastReport.Base/Utils/FRWriter.cs
@@ -74,6 +74,7 @@ public class FRWriter : IDisposable
//private StringBuilder FText;
private object diffObject;
private bool saveChildren;
+ private bool saveExternalPages;
private bool writeHeader;
private BlobStore blobStore;
private SerializeTo serializeTo;
@@ -124,6 +125,15 @@ public bool SaveChildren
set { saveChildren = value; }
}
+ ///
+ /// Gets or sets a value that determines whether is necessary to serialize external pages.
+ ///
+ public bool SaveExternalPages
+ {
+ get { return saveExternalPages; }
+ set { saveExternalPages = value; }
+ }
+
///
/// Gets or sets a value that determines whether is necessary to add xml header.
///
diff --git a/FastReport.Base/Utils/FontManager.Gdi.cs b/FastReport.Base/Utils/FontManager.Gdi.cs
index 6e7ce128..5be5eb31 100644
--- a/FastReport.Base/Utils/FontManager.Gdi.cs
+++ b/FastReport.Base/Utils/FontManager.Gdi.cs
@@ -2,6 +2,7 @@
#if !SKIA && !FRCORE && (!MONO || WPF)
using System;
using System.Diagnostics;
+using System.Drawing;
using System.Drawing.Text;
using System.IO;
@@ -19,8 +20,15 @@ public static bool AddFont(string filename)
bool success = false;
if (File.Exists(filename))
{
- PrivateFontCollection.AddFontFile(filename);
- success = true;
+
+ bool isInstalled = CheckFontIsInstalled(filename);
+
+ if (!isInstalled)
+ {
+ PrivateFontCollection.AddFontFile(filename);
+
+ success = true;
+ }
}
else
{
@@ -29,6 +37,28 @@ public static bool AddFont(string filename)
return success;
}
+ ///
+ /// Checks whether the font from the specified file is installed in the system.
+ ///
+ /// The path to the font file.
+ /// Returns true if the font is installed on the system, otherwise false.
+ public static bool CheckFontIsInstalled(string filename)
+ {
+ PrivateFontCollection tempFontCollection = new PrivateFontCollection();
+ tempFontCollection.AddFontFile(filename);
+ string fontName = tempFontCollection.Families[0].Name;
+
+
+ InstalledFontCollection installedFonts = new InstalledFontCollection();
+ FontFamily[] fontFamilies = installedFonts.Families;
+
+ // Checking if a font named FontName is installed in the system
+ // Array.Exists checks if an element in the array exists that satisfies the condition
+ bool isInstalled = Array.Exists(fontFamilies, family => family.Name.Equals(fontName, StringComparison.OrdinalIgnoreCase));
+
+ return isInstalled;
+ }
+
///
/// Adds a font contained in system memory to this collection.
///
diff --git a/FastReport.Base/Utils/HtmlTextRenderer.cs b/FastReport.Base/Utils/HtmlTextRenderer.cs
index 185d3373..849d90d5 100644
--- a/FastReport.Base/Utils/HtmlTextRenderer.cs
+++ b/FastReport.Base/Utils/HtmlTextRenderer.cs
@@ -576,6 +576,36 @@ private void CssStyle(StyleDescriptor style, Dictionary dict)
if (dict == null)
return;
string tStr;
+
+ // If "font-style" contains "italic" or "oblique", apply the Italic style to the text.
+ if (dict.TryGetValue("font-style", out tStr))
+ {
+ if (tStr.Contains("italic") || tStr.Contains("oblique"))
+ style.FontStyle |= FontStyle.Italic;
+ }
+
+ // If "font-weight" contains "bold", apply the Bold style to the text.
+ if (dict.TryGetValue("font-weight", out tStr))
+ {
+ if (tStr.Contains("bold"))
+ style.FontStyle |= FontStyle.Bold;
+ }
+
+ // If "text-decoration" contains both "underline" and "line-through", apply both styles to the text.
+ // Otherwise, check and apply each style individually.
+ if (dict.TryGetValue("text-decoration", out tStr))
+ {
+ if (tStr.Contains("underline") && tStr.Contains("line-through"))
+ style.FontStyle |= FontStyle.Underline | FontStyle.Strikeout;
+ else
+ {
+ if (tStr.Contains("underline"))
+ style.FontStyle |= FontStyle.Underline;
+ if (tStr.Contains("line-through"))
+ style.FontStyle |= FontStyle.Strikeout;
+ }
+ }
+
if (dict.TryGetValue("font-size", out tStr))
{
if (EndsWith(tStr, "px"))
@@ -3066,6 +3096,26 @@ public void ToHtml(FastString sb, bool close)
if (color.A > 0) sb.Append(String.Format(CultureInfo, "color:rgba({0},{1},{2},{3});", color.R, color.G, color.B, ((float)color.A) / 255f));
if (font != null) { sb.Append("font-family:"); sb.Append(font.Name); sb.Append(";"); }
if (fontsize > 0) { sb.Append("font-size:"); sb.Append(fontsize.ToString(CultureInfo)); sb.Append("pt;"); }
+
+ //if ((fontStyle & FontStyle.Italic) == FontStyle.Italic) { sb.Append("font-style:italic;"); }
+ //if ((fontStyle & FontStyle.Bold) == FontStyle.Bold) { sb.Append("font-weight:bold;"); }
+
+ //bool underline = (fontStyle & FontStyle.Underline) == FontStyle.Underline;
+ //bool strikeout = (fontStyle & FontStyle.Strikeout) == FontStyle.Strikeout;
+
+ //if (underline && strikeout)
+ //{
+ // sb.Append("text-decoration:underline line-through;");
+ //}
+ //else if (underline)
+ //{
+ // sb.Append("text-decoration: underline;");
+ //}
+ //else if (strikeout)
+ //{
+ // sb.Append("text-decoration:line-through;");
+ //}
+
sb.Append("\">");
}
}
diff --git a/FastReport.Base/Utils/ImageHelper.Async.cs b/FastReport.Base/Utils/ImageHelper.Async.cs
new file mode 100644
index 00000000..68ff9868
--- /dev/null
+++ b/FastReport.Base/Utils/ImageHelper.Async.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+#if NETCOREAPP
+using System.Net.Http;
+#endif
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport.Utils
+{
+ public static partial class ImageHelper
+ {
+ internal static async Task LoadAsync(string fileName, CancellationToken cancellationToken)
+ {
+ if (!String.IsNullOrEmpty(fileName))
+#if NETCOREAPP
+ return await File.ReadAllBytesAsync(fileName, cancellationToken);
+#else
+ return File.ReadAllBytes(fileName);
+#endif
+ return null;
+ }
+
+ internal static async Task LoadURLAsync(Uri url, CancellationToken cancellationToken)
+ {
+#if NETCOREAPP
+ using (var httpClient = new HttpClient())
+ {
+ return await httpClient.GetByteArrayAsync(url, cancellationToken);
+ }
+#else
+ ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
+ using (var web = new WebClient())
+ {
+ return await web.DownloadDataTaskAsync(url);
+ }
+#endif
+ }
+ }
+}
diff --git a/FastReport.Base/Utils/ImageHelper.cs b/FastReport.Base/Utils/ImageHelper.cs
index 701f62ad..5555cd20 100644
--- a/FastReport.Base/Utils/ImageHelper.cs
+++ b/FastReport.Base/Utils/ImageHelper.cs
@@ -46,7 +46,7 @@ public interface IImageHelperLoader
/// Internal calss for image processing
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public static class ImageHelper
+ public static partial class ImageHelper
{
private readonly static object _customLoadersLocker = new object();
private readonly static List _customLoaders = new List();
diff --git a/FastReport.Base/Utils/TextRenderer.cs b/FastReport.Base/Utils/TextRenderer.cs
index 130b3ebf..133a23e4 100644
--- a/FastReport.Base/Utils/TextRenderer.cs
+++ b/FastReport.Base/Utils/TextRenderer.cs
@@ -2110,7 +2110,7 @@ public class InlineImageCache : IDisposable
private bool serialized;
- private object locker;
+ private readonly object locker;
#endregion Private Fields
diff --git a/FastReport.Base/ZipCodeObject.Async.cs b/FastReport.Base/ZipCodeObject.Async.cs
new file mode 100644
index 00000000..e8523950
--- /dev/null
+++ b/FastReport.Base/ZipCodeObject.Async.cs
@@ -0,0 +1,20 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastReport
+{
+ public partial class ZipCodeObject
+ {
+
+ #region Report Engine
+
+ ///
+ public override async Task GetDataAsync(CancellationToken cancellationToken)
+ {
+ await base.GetDataAsync(cancellationToken);
+ GetDataShared();
+ }
+
+ #endregion
+ }
+}
diff --git a/FastReport.Base/ZipCodeObject.cs b/FastReport.Base/ZipCodeObject.cs
index 0b327e42..653ec056 100644
--- a/FastReport.Base/ZipCodeObject.cs
+++ b/FastReport.Base/ZipCodeObject.cs
@@ -338,6 +338,11 @@ public override string[] GetExpressions()
public override void GetData()
{
base.GetData();
+ GetDataShared();
+ }
+
+ private void GetDataShared()
+ {
if (!String.IsNullOrEmpty(DataColumn))
{
object value = Report.GetColumnValue(DataColumn);
diff --git a/FastReport.Compat/Directory.Build.props b/FastReport.Compat/Directory.Build.props
index cd21a44c..f2978f3c 100644
--- a/FastReport.Compat/Directory.Build.props
+++ b/FastReport.Compat/Directory.Build.props
@@ -13,7 +13,8 @@
GIT
true
Common compatible types for FastReport .Net, Core and Mono
- frlogo192.png
+ frlogo192.png
+ $(PackageIconFileName)
true
..\..\FastReport.OpenSource.snk
@@ -33,7 +34,7 @@
-
+
True
false
diff --git a/FastReport.Core.Web/Application/Cache/IWebReportCache.cs b/FastReport.Core.Web/Application/Cache/IWebReportCache.cs
index a73a44c8..d8ce4a50 100644
--- a/FastReport.Core.Web/Application/Cache/IWebReportCache.cs
+++ b/FastReport.Core.Web/Application/Cache/IWebReportCache.cs
@@ -2,8 +2,10 @@
namespace FastReport.Web.Cache
{
-
- internal interface IWebReportCache : IDisposable
+ ///
+ /// Represents the cache where all webReports will be stored
+ ///
+ public interface IWebReportCache : IDisposable
{
void Add(WebReport webReport);
diff --git a/FastReport.Core.Web/Application/DesignerSettings.cs b/FastReport.Core.Web/Application/DesignerSettings.cs
index 59986591..4ebbdfda 100644
--- a/FastReport.Core.Web/Application/DesignerSettings.cs
+++ b/FastReport.Core.Web/Application/DesignerSettings.cs
@@ -19,7 +19,7 @@ public class DesignerSettings
public bool ScriptCode { get; set; } = false;
///
- /// Gets or sets the text of configuration of Online Designer
+ /// Gets or sets the json of configuration of Online Designer
///
public string Config { get; set; } = "";
diff --git a/FastReport.Core.Web/Application/Infrastructure/ControllerBuilder.cs b/FastReport.Core.Web/Application/Infrastructure/ControllerBuilder.cs
index 9e30a594..52bb93bf 100644
--- a/FastReport.Core.Web/Application/Infrastructure/ControllerBuilder.cs
+++ b/FastReport.Core.Web/Application/Infrastructure/ControllerBuilder.cs
@@ -255,7 +255,7 @@ public static Task HandleResult(HttpContext httpContext, object result)
{
Debug.Assert(result.GetType() != typeof(IActionResult));
- string content = System.Text.Json.JsonSerializer.Serialize(result);
+ string content = JsonSerializer.Serialize(result);
var contentResult = Results.Content(content, "text/json");
return contentResult.ExecuteAsync(httpContext);
}
diff --git a/FastReport.Core.Web/Application/LinkerFlags.cs b/FastReport.Core.Web/Application/LinkerFlags.cs
index aaaa0583..03b9634f 100644
--- a/FastReport.Core.Web/Application/LinkerFlags.cs
+++ b/FastReport.Core.Web/Application/LinkerFlags.cs
@@ -11,97 +11,4 @@ internal static class LinkerFlags
internal const DynamicallyAccessedMemberTypes All = DynamicallyAccessedMemberTypes.All;
}
-
-#if !NET5_0_OR_GREATER
- [AttributeUsage(
- AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
- AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method |
- AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
- Inherited = false)]
- internal sealed class DynamicallyAccessedMembersAttribute : Attribute
- {
- ///
- /// Initializes a new instance of the class
- /// with the specified member types.
- ///
- /// The types of members dynamically accessed.
- public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
- {
- }
- }
-
- //
- // Summary:
- // Specifies the types of members that are dynamically accessed. This enumeration
- // has a System.FlagsAttribute attribute that allows a bitwise combination of its
- // member values.
- [Flags]
- internal enum DynamicallyAccessedMemberTypes
- {
- //
- // Summary:
- // Specifies all members.
- All = -1,
- //
- // Summary:
- // Specifies no members.
- None = 0,
- //
- // Summary:
- // Specifies the default, parameterless public constructor.
- PublicParameterlessConstructor = 1,
- //
- // Summary:
- // Specifies all public constructors.
- PublicConstructors = 3,
- //
- // Summary:
- // Specifies all non-public constructors.
- NonPublicConstructors = 4,
- //
- // Summary:
- // Specifies all public methods.
- PublicMethods = 8,
- //
- // Summary:
- // Specifies all non-public methods.
- NonPublicMethods = 16,
- //
- // Summary:
- // Specifies all public fields.
- PublicFields = 32,
- //
- // Summary:
- // Specifies all non-public fields.
- NonPublicFields = 64,
- //
- // Summary:
- // Specifies all public nested types.
- PublicNestedTypes = 128,
- //
- // Summary:
- // Specifies all non-public nested types.
- NonPublicNestedTypes = 256,
- //
- // Summary:
- // Specifies all public properties.
- PublicProperties = 512,
- //
- // Summary:
- // Specifies all non-public properties.
- NonPublicProperties = 1024,
- //
- // Summary:
- // Specifies all public events.
- PublicEvents = 2048,
- //
- // Summary:
- // Specifies all non-public events.
- NonPublicEvents = 4096,
- //
- // Summary:
- // Specifies all interfaces implemented by the type.
- Interfaces = 8192
- }
-#endif
}
diff --git a/FastReport.Core.Web/Application/WebReport.Backend.cs b/FastReport.Core.Web/Application/WebReport.Backend.cs
index 77152d3c..47a6df82 100644
--- a/FastReport.Core.Web/Application/WebReport.Backend.cs
+++ b/FastReport.Core.Web/Application/WebReport.Backend.cs
@@ -13,6 +13,24 @@ namespace FastReport.Web
public partial class WebReport
{
+ private string localizationFile;
+
+
+ ///
+ /// Gets or sets the WebReport's locale
+ ///
+ public string LocalizationFile
+ {
+ get => localizationFile;
+ set
+ {
+ localizationFile = value;
+ string path = WebUtils.MapPath(localizationFile);
+ Res.LoadLocale(path);
+ }
+ }
+
+
internal static IResourceLoader ResourceLoader { get; set; }
diff --git a/FastReport.Core.Web/Application/WebReport.Tabs.cs b/FastReport.Core.Web/Application/WebReport.Tabs.cs
index e17fa5ab..8940263e 100644
--- a/FastReport.Core.Web/Application/WebReport.Tabs.cs
+++ b/FastReport.Core.Web/Application/WebReport.Tabs.cs
@@ -48,6 +48,12 @@ public int CurrentTabIndex
///
public bool SplitReportPagesInTabs { get; set; } = false;
+ ///
+ /// Gets or sets a value indicating whether the report name is displayed in the tab.
+ /// If set to true, the tab will display the report name. If false, it will display report parameters.
+ /// Default value: false.
+ ///
+ public bool ShowReportNameInTab { get; set; } = false;
///
/// List of report tabs
@@ -122,19 +128,17 @@ internal string GetCurrentTabName()
internal string GetTabName(int i)
{
-
- if (String.IsNullOrEmpty(Tabs[i].Name))
+ if (string.IsNullOrEmpty(Tabs[i].Name) || ShowReportNameInTab)
{
- string s = Tabs[i].Report.ReportInfo.Name;
- if (String.IsNullOrEmpty(s))
+ var s = Tabs[i].Report.ReportInfo.Name;
+ if (string.IsNullOrEmpty(s))
s = Path.GetFileNameWithoutExtension(Tabs[i].Report.FileName);
- if (String.IsNullOrEmpty(s))
+ if (string.IsNullOrEmpty(s))
s = (i + 1).ToString();
return s;
-
}
- else
- return Tabs[i].Name;
+
+ return Tabs[i].Name;
}
diff --git a/FastReport.Core.Web/Application/WebReport.cs b/FastReport.Core.Web/Application/WebReport.cs
index 0628cef2..9f302cc7 100644
--- a/FastReport.Core.Web/Application/WebReport.cs
+++ b/FastReport.Core.Web/Application/WebReport.cs
@@ -26,8 +26,6 @@ public enum WebReportMode
public partial class WebReport
{
- private string localizationFile;
-
#if DIALOGS
internal Dialog Dialog { get; }
#endif
@@ -55,19 +53,12 @@ public Report Report
set => Tabs[CurrentTabIndex].Report = value;
}
- ///
- /// Gets or sets the WebReport's locale
- ///
- public string LocalizationFile
- {
- get => localizationFile;
- set
- {
- localizationFile = value;
- string path = WebUtils.MapPath(localizationFile);
- Res.LoadLocale(path);
- }
- }
+#if WASM
+ [Obsolete("Doesn't support in Wasm. Please, use SetLocalization(Stream) instead", true)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string LocalizationFile { get; set; }
+#endif
+
internal IWebRes Res { get; } = new WebRes();
@@ -222,9 +213,9 @@ internal string InlineStyle
public WebReport()
{
+#if !WASM
string path = WebUtils.MapPath(LocalizationFile);
Res.LoadLocale(path);
-#if !WASM
WebReportCache.Instance?.Add(this);
#endif
#if DIALOGS
@@ -237,6 +228,14 @@ static WebReport()
ScriptSecurity = new ScriptSecurity(new ScriptChecker());
}
+ ///
+ /// Sets WebReport localization using
+ ///
+ /// Stream with localization in `*.frl` format
+ public void SetLocalization(Stream stream)
+ {
+ Res.LoadLocale(stream);
+ }
public void LoadPrepared(string filename)
{
diff --git a/FastReport.Core.Web/Application/WebUtils.cs b/FastReport.Core.Web/Application/WebUtils.cs
index 1ac63130..3d9626bf 100644
--- a/FastReport.Core.Web/Application/WebUtils.cs
+++ b/FastReport.Core.Web/Application/WebUtils.cs
@@ -34,6 +34,7 @@ internal static bool ShouldExportUseZipFormat(IEnumerable> exportParams, Exports export)
=> ShouldUseZipFormat(exportParams, export);
+#if !WASM
internal static string MapPath(string path)
{
if (path.IsNullOrWhiteSpace())
@@ -41,11 +42,9 @@ internal static string MapPath(string path)
if (Path.IsPathRooted(path))
return path;
-#if !WASM
return Path.Combine(FastReportGlobal.HostingEnvironment.ContentRootPath, path);
-#endif
- return string.Empty;
}
+#endif
internal static string ToUrl(params string[] segments)
{
@@ -85,7 +84,7 @@ internal static bool IsPng(byte[] image)
{
byte[] pngHeader = new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 };
bool isPng = true;
- for (int i = 0; i < 8; i++)
+ for (int i = 0; i < 8 && image.Length > 7; i++)
if (image[i] != pngHeader[i])
{
isPng = false;
diff --git a/FastReport.Core.Web/Controllers/Designer/ConnectionsController.cs b/FastReport.Core.Web/Controllers/Designer/ConnectionsController.cs
index 738bf244..ca9fa61c 100644
--- a/FastReport.Core.Web/Controllers/Designer/ConnectionsController.cs
+++ b/FastReport.Core.Web/Controllers/Designer/ConnectionsController.cs
@@ -24,8 +24,8 @@ public sealed class ConnectionsParams
public string ConnectionString { get; set; }
}
- public sealed class ConnectionTablesRequestModel
- {
+ public sealed class ConnectionTablesRequestModel
+ {
public ConnectionsParams ConnectionsParams { get; set; }
public List CustomViews { get; set; }
@@ -37,9 +37,8 @@ public static IResult GetConnectionTypes([FromQuery] string needSqlSupportInfo,
var isNeedSqlSupport = bool.TryParse(needSqlSupportInfo, out var parsedBool) && parsedBool;
var response = connectionsService.GetConnectionTypes(isNeedSqlSupport);
- var content = "{" + string.Join(",", response.ToArray()) + "}";
- return Results.Content(content, "application/json");
+ return Results.Json(response);
}
[Obsolete]
@@ -84,6 +83,54 @@ public static IResult MakeConnectionString(string connectionType,
: Results.Content(response, "application/xml");
}
+
+ [HttpPost("/designer.updateConnectionTable")]
+ public static IResult UpdateConnectionTable([FromQuery] string reportId,
+ [FromQuery] string connectionType,
+ [FromBody] UpdateTableParams parameters,
+ IReportService reportService,
+ IConnectionsService connectionsService,
+ HttpRequest request)
+ {
+ if (!IsAuthorized(request))
+ return Results.Unauthorized();
+
+ try
+ {
+ string response;
+
+ if (parameters.ConnectionString.IsNullOrWhiteSpace())
+ {
+ if (!reportService.TryFindWebReport(reportId, out var webReport))
+ return Results.NotFound();
+
+ response = connectionsService.GetUpdatedTableByReportId(webReport, parameters);
+ }
+ else
+ {
+ response = connectionsService.GetUpdatedTableByConnectionString(parameters.ConnectionString,
+ connectionType, parameters);
+ }
+
+ return Results.Content(response, "application/xml");
+ }
+ catch (Exception ex)
+ {
+ return Results.BadRequest(ex.Message);
+ }
+ }
+
+ [HttpGet("/designer.getParameterTypes")]
+ public static IResult GetParameterTypes([FromQuery] string connectionType,
+ IConnectionsService connectionsService)
+ {
+ var response = connectionsService.GetParameterTypes(connectionType, out string error);
+
+ return string.IsNullOrEmpty(error) ?
+ Results.Json(response)
+ : Results.BadRequest(error);
+ }
+
[HttpGet("/designer.getConnectionStringProperties")]
public static IResult GetConnectionStringProperties([FromQuery] ConnectionsParams query,
IConnectionsService connectionsService)
diff --git a/FastReport.Core.Web/Controllers/Preview/GetReportController.cs b/FastReport.Core.Web/Controllers/Preview/GetReportController.cs
index 800fd0b4..f796138b 100644
--- a/FastReport.Core.Web/Controllers/Preview/GetReportController.cs
+++ b/FastReport.Core.Web/Controllers/Preview/GetReportController.cs
@@ -6,6 +6,7 @@
using System.Net.Mime;
using System.Threading.Tasks;
using FastReport.Utils;
+using System.Threading;
namespace FastReport.Web.Controllers
{
@@ -14,9 +15,10 @@ static partial class Controllers
private const string INVALID_REPORT_MESSAGE = "Error loading report: The report structure is invalid.";
[HttpPost("/preview.getReport")]
- public static IResult GetReport([FromQuery] string reportId,
+ public static async Task GetReport([FromQuery] string reportId,
IReportService reportService,
- HttpRequest request)
+ HttpRequest request,
+ CancellationToken cancellationToken)
{
if (!IsAuthorized(request))
return Results.Unauthorized();
@@ -28,7 +30,7 @@ public static IResult GetReport([FromQuery] string reportId,
try
{
- string render = reportService.GetReport(webReport, query);
+ string render = await reportService.GetReportAsync(webReport, query, cancellationToken);
if (render.IsNullOrEmpty())
return Results.Ok();
diff --git a/FastReport.Core.Web/Directory.Build.targets b/FastReport.Core.Web/Directory.Build.targets
index 18cedec9..bd0a6a03 100644
--- a/FastReport.Core.Web/Directory.Build.targets
+++ b/FastReport.Core.Web/Directory.Build.targets
@@ -3,9 +3,9 @@
-
+
-
+
\ No newline at end of file
diff --git a/FastReport.Core.Web/Services/Abstract/IConnectionsService.cs b/FastReport.Core.Web/Services/Abstract/IConnectionsService.cs
index bcefe128..ec5b99e3 100644
--- a/FastReport.Core.Web/Services/Abstract/IConnectionsService.cs
+++ b/FastReport.Core.Web/Services/Abstract/IConnectionsService.cs
@@ -34,9 +34,34 @@ public interface IConnectionsService
/// Returns JSON with connected tables
string GetConnectionTables(string connectionType, string connectionString, List customConnections);
+ ///
+ /// Updates a table within a web report based on the provided parameters.
+ ///
+ /// The WebReport object containing the table to be updated.
+ /// The parameters specifying the table name, SQL query, and update parameters.
+ /// A string representation of the updated table.
+ string GetUpdatedTableByReportId(WebReport webReport, UpdateTableParams parameters);
+
+ ///
+ /// Updates a table within a database using the provided connection string and parameters.
+ ///
+ /// The connection string to the database.
+ /// The type of connection.
+ /// The parameters specifying the table name, SQL query, and update parameters.
+ /// A string representation of the updated table.
+ string GetUpdatedTableByConnectionString(string connectionString, string connectionType, UpdateTableParams parameters);
+
///
/// Returns the list of connection types
///
- List GetConnectionTypes(bool needSqlSupportInfo = false);
+ Dictionary GetConnectionTypes(bool needSqlSupportInfo = false);
+
+ ///
+ /// Returns the list of parameter types by connection type.
+ ///
+ /// The type of connection.
+ /// Error message.
+ ///
+ Dictionary GetParameterTypes(string connectionType, out string errorMsg);
}
}
diff --git a/FastReport.Core.Web/Services/Abstract/IReportService.cs b/FastReport.Core.Web/Services/Abstract/IReportService.cs
index 4dc45d44..df6ce2ce 100644
--- a/FastReport.Core.Web/Services/Abstract/IReportService.cs
+++ b/FastReport.Core.Web/Services/Abstract/IReportService.cs
@@ -15,14 +15,6 @@ namespace FastReport.Web.Services
///
public interface IReportService
{
- ///
- /// Returns a report for Preview on the Web
- ///
- /// Report a preview of which you want to create
- /// Report preview creation options
- /// Returns the HTML string of the report preview
- string GetReport(WebReport webReport, GetReportServiceParams @params);
-
///
/// Asynchronously returns a report for Preview on the Web
///
diff --git a/FastReport.Core.Web/Services/Implementation/ConnectionService.cs b/FastReport.Core.Web/Services/Implementation/ConnectionService.cs
index 56277ef6..7b7f72cf 100644
--- a/FastReport.Core.Web/Services/Implementation/ConnectionService.cs
+++ b/FastReport.Core.Web/Services/Implementation/ConnectionService.cs
@@ -8,7 +8,6 @@
using System.Data;
using System.IO;
using System.Linq;
-using System.Net;
using System.Text;
using System.Text.Encodings.Web;
@@ -16,20 +15,22 @@ namespace FastReport.Web.Services
{
internal sealed class ConnectionService : IConnectionsService
{
-
- public string GetConnectionStringPropertiesJSON(string connectionType, string connectionString, out bool isError)
+ private static Type GetConnectionType(string connectionType)
{
var objects = new List();
RegisteredObjects.DataConnections.EnumItems(objects);
- Type connType = null;
foreach (var info in objects)
- if (info.Object != null &&
- info.Object.FullName == connectionType)
+ if (info.Object != null && info.Object.FullName == connectionType)
{
- connType = info.Object;
- break;
+ return info.Object;
}
+ return null;
+ }
+
+ public string GetConnectionStringPropertiesJSON(string connectionType, string connectionString, out bool isError)
+ {
+ Type connType = GetConnectionType(connectionType);
if (connType == null)
{
@@ -67,8 +68,8 @@ public string GetConnectionStringPropertiesJSON(string connectionType, string co
try
{
object owner = conn;
- if (conn is ICustomTypeDescriptor)
- owner = ((ICustomTypeDescriptor)conn).GetPropertyOwner(pd);
+ if (conn is ICustomTypeDescriptor customTypeDescriptor)
+ owner = customTypeDescriptor.GetPropertyOwner(pd);
value = pd.GetValue(owner);
}
catch { }
@@ -95,17 +96,7 @@ public string GetConnectionStringPropertiesJSON(string connectionType, string co
public string CreateConnectionStringJSON(string connectionType, IFormCollection form, out bool isError)
{
- var objects = new List();
- RegisteredObjects.DataConnections.EnumItems(objects);
- Type connType = null;
-
- foreach (var info in objects)
- if (info.Object != null &&
- info.Object.FullName == connectionType)
- {
- connType = info.Object;
- break;
- }
+ Type connType = GetConnectionType(connectionType);
if (connType == null)
{
@@ -141,8 +132,8 @@ public string CreateConnectionStringJSON(string connectionType, IFormCollection
object value = typeConverter.ConvertFromString(propertyValue);
object owner = conn;
- if (conn is ICustomTypeDescriptor)
- owner = ((ICustomTypeDescriptor)conn).GetPropertyOwner(pd);
+ if (conn is ICustomTypeDescriptor customTypeDescriptor)
+ owner = customTypeDescriptor.GetPropertyOwner(pd);
pd.SetValue(owner, value);
}
catch (Exception ex)
@@ -166,73 +157,138 @@ public string CreateConnectionStringJSON(string connectionType, IFormCollection
public string GetConnectionTables(string connectionType, string connectionString, List customViews)
{
if (!IsConnectionStringValid(connectionString, out var errorMsg))
- {
throw new Exception(errorMsg);
- }
- var objects = new List();
- RegisteredObjects.DataConnections.EnumItems(objects);
- Type connType = null;
+ try
+ {
+ using var conn = CreateConnection(connectionType);
- foreach (var info in objects)
- if (info.Object != null &&
- info.Object.FullName == connectionType)
+ conn.ConnectionString = connectionString;
+
+ if (FastReportGlobal.AllowCustomSqlQueries)
{
- connType = info.Object;
- break;
+ foreach (var view in customViews)
+ {
+ var source = new TableDataSource
+ {
+ Table = new DataTable(),
+ TableName = view.TableName,
+ Name = view.TableName,
+ SelectCommand = view.SqlQuery
+ };
+
+ conn.Tables.Add(source);
+ conn.DataSet.Tables.Add(source.Table);
+ }
}
- if (connType != null)
+ conn.CreateAllTables(true);
+
+ foreach (TableDataSource c in conn.Tables)
+ c.Enabled = true;
+
+ return SerializeToString(conn);
+ }
+ catch
+ {
+ throw new Exception("Error in creating tables. Please verify your connection string.");
+ }
+ }
+
+ public string GetUpdatedTableByReportId(WebReport webReport, UpdateTableParams parameters)
+ {
+ var dataSource = webReport.Report.GetDataSource(parameters.TableName) as TableDataSource
+ ?? throw new Exception("Table not found");
+
+ try
{
- try
+ foreach (var parameter in parameters.Parameters)
{
- using (DataConnectionBase conn = (DataConnectionBase)Activator.CreateInstance(connType))
- using (var writer = new FRWriter())
- {
- conn.ConnectionString = connectionString;
+ ApplyParameterToDataSource(dataSource, parameter);
+ }
- if (FastReportGlobal.AllowCustomSqlQueries)
- {
- foreach (var view in customViews)
- {
- var source = new TableDataSource
- {
- Table = new DataTable(),
- TableName = view.TableName,
- Name = view.TableName,
- SelectCommand = view.SqlQuery
- };
-
- conn.Tables.Add(source);
- conn.DataSet.Tables.Add(source.Table);
- }
- }
+ dataSource.SelectCommand = parameters.SqlQuery;
+ dataSource.RefreshTable();
- conn.CreateAllTables(true);
- foreach (TableDataSource c in conn.Tables)
- c.Enabled = true;
+ return SerializeToString(dataSource);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("Error in creating tables. Please verify your parameters.", ex);
+ }
+ }
- writer.SaveChildren = true;
- writer.WriteHeader = false;
- writer.Write(conn);
+ public string GetUpdatedTableByConnectionString(string connectionString, string connectionType,
+ UpdateTableParams parameters)
+ {
+ if (!IsConnectionStringValid(connectionString, out var errorMsg))
+ throw new ArgumentException(errorMsg);
- using (var ms = new MemoryStream())
- {
- writer.Save(ms);
- ms.Position = 0;
+ try
+ {
+ using var conn = CreateConnection(connectionType);
+ conn.ConnectionString = connectionString;
+ conn.CreateAllTables(true);
- return Encoding.UTF8.GetString(ms.ToArray());
- }
- }
- }
- catch
+ var dataSource = conn.Tables.Cast()
+ .FirstOrDefault(table => string.Equals(table.TableName, parameters.TableName))
+ ?? throw new ArgumentException("Table not found");
+
+ foreach (var parameter in parameters.Parameters)
{
- throw new Exception("Error in creating tables. Please verify your connection string.");
+ ApplyParameterToDataSource(dataSource, parameter);
}
+
+ dataSource.SelectCommand = parameters.SqlQuery;
+ dataSource.RefreshTable();
+
+ return SerializeToString(dataSource);
}
+ catch (Exception ex)
+ {
+ throw new Exception("Error updating table in the database.", ex);
+ }
+ }
- throw new Exception("Connection type not found");
+ private static CommandParameter CreateParameter(ParameterModel parameterParams)
+ {
+ return new CommandParameter
+ {
+ Name = parameterParams.Name ?? string.Empty,
+ Value = parameterParams.Value ?? string.Empty,
+ DataType = parameterParams.DataType,
+ Expression = parameterParams.Expression ?? string.Empty
+ };
+ }
+
+ private static DataConnectionBase CreateConnection(string connectionType)
+ {
+ var connType = GetConnectionType(connectionType);
+ return connType == null
+ ? throw new ArgumentException("Connection type not found")
+ : (DataConnectionBase)Activator.CreateInstance(connType);
+ }
+
+ private static string SerializeToString(IFRSerializable serializable)
+ {
+ using var writer = new FRWriter();
+ writer.SaveChildren = true;
+ writer.WriteHeader = false;
+ writer.Write(serializable);
+
+ using var ms = new MemoryStream();
+ writer.Save(ms);
+ ms.Position = 0;
+
+ using var reader = new StreamReader(ms, Encoding.UTF8);
+ return reader.ReadToEnd();
+ }
+
+ private static void ApplyParameterToDataSource(TableDataSource dataSource, ParameterModel parameterModel)
+ {
+ var parameter = CreateParameter(parameterModel);
+ dataSource.Parameters.Add(parameter);
}
private static bool IsConnectionStringValid(string connectionString, out string errorMsg)
@@ -247,26 +303,62 @@ private static bool IsConnectionStringValid(string connectionString, out string
return true;
}
- public List GetConnectionTypes(bool needSqlSupportInfo = false)
+ public Dictionary GetConnectionTypes(bool needSqlSupportInfo = false)
{
- var result = new List();
+ var result = new Dictionary();
var objects = new List();
RegisteredObjects.DataConnections.EnumItems(objects);
foreach (var info in objects.Where(info => info.Object != null))
{
- string connection;
-
if (needSqlSupportInfo)
{
using var conn = (DataConnectionBase)Activator.CreateInstance(info.Object);
- connection = $"\"{info.Object.FullName}\": {GetConnectionJson(info.Text, conn.IsSqlBased)}";
+ result.Add(info.Object.FullName, GetConnectionJson(info.Text, conn.IsSqlBased));
}
- else connection = $"\"{info.Object.FullName}\": \"{Res.TryGetBuiltin(info.Text)}\"";
+ else
+ result.Add(info.Object.FullName, Res.TryGetBuiltin(info.Text));
+ }
+
+ return result;
+ }
+
+ public Dictionary GetParameterTypes(string connectionType, out string errorMsg)
+ {
+ var result = new Dictionary();
+ Type connType = GetConnectionType(connectionType);
+
+ if (connType == null)
+ {
+ errorMsg = "Connection type not found";
+ return result;
+ }
- result.Add(connection);
+ try
+ {
+ using (var conn = (DataConnectionBase)Activator.CreateInstance(connType))
+ {
+ Array values;
+
+ var paramType = conn.GetParameterType();
+ if (paramType != null)
+ values = Enum.GetValues(paramType);
+ else
+ values = Enum.GetValues();
+
+ foreach (var par in values)
+ {
+ result.Add(par.ToString(), (int)par);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ errorMsg = ex.ToString();
+ return result;
}
+ errorMsg = "";
return result;
}
diff --git a/FastReport.Core.Web/Services/Implementation/InternalResourceLoader.cs b/FastReport.Core.Web/Services/Implementation/InternalResourceLoader.cs
index cba49fef..7b4beaf7 100644
--- a/FastReport.Core.Web/Services/Implementation/InternalResourceLoader.cs
+++ b/FastReport.Core.Web/Services/Implementation/InternalResourceLoader.cs
@@ -78,6 +78,7 @@ public byte[] GetBytes(string name)
var buffer = new byte[resourceStream.Length];
resourceStream.Read(buffer, 0, buffer.Length);
+
cache2[name] = buffer;
return buffer;
}
@@ -93,7 +94,7 @@ public async ValueTask GetBytesAsync(string name, CancellationToken canc
return null;
var buffer = new byte[resourceStream.Length];
- await resourceStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
+ await resourceStream.ReadAsync(buffer, cancellationToken);
cache2[name] = buffer;
return buffer;
}
diff --git a/FastReport.Core.Web/Services/Implementation/ReportService.cs b/FastReport.Core.Web/Services/Implementation/ReportService.cs
index d4c3c199..7399d9e9 100644
--- a/FastReport.Core.Web/Services/Implementation/ReportService.cs
+++ b/FastReport.Core.Web/Services/Implementation/ReportService.cs
@@ -21,7 +21,8 @@ public ReportService(IWebReportCache webReportCache)
_cache = webReportCache;
}
- public string GetReport(WebReport webReport, GetReportServiceParams @params)
+
+ public async Task GetReportAsync(WebReport webReport, GetReportServiceParams @params, CancellationToken cancellationToken = default)
{
#if DIALOGS
webReport.Dialogs(@params.DialogParams);
@@ -31,13 +32,16 @@ public string GetReport(WebReport webReport, GetReportServiceParams @params)
if (webReport.Mode == WebReportMode.Dialog)
{
- webReport.Report.PreparePhase1();
+ await webReport.Report.PreparePhase1Async(cancellationToken);
}
else
#endif
if (!webReport.ReportPrepared && @params.SkipPrepare != "yes" || @params.ForceRefresh == "yes")
{
- webReport.Report.Prepare();
+ // don't reset the data state if we run the dialog page or refresh a report.
+ // This is necessary to keep data filtering settings alive
+ var resetDataState = @params.ForceRefresh != "yes" && string.IsNullOrEmpty(@params.DialogParams.DialogN);
+ await webReport.Report.PrepareAsync(false, resetDataState, cancellationToken);
webReport.SplitReportPagesByTabs();
}
@@ -54,11 +58,6 @@ public string GetReport(WebReport webReport, GetReportServiceParams @params)
return webReport.Render(renderBodyBool).ToString();
}
- public Task GetReportAsync(WebReport webReport, GetReportServiceParams @params, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(GetReport(webReport, @params));
- }
-
public async Task InvokeCustomElementAction(WebReport webReport, string elementId, string inputValue)
{
var element = webReport.Toolbar.Elements.FirstOrDefault(e =>
diff --git a/FastReport.Core.Web/Services/ServicesParamsModels.cs b/FastReport.Core.Web/Services/ServicesParamsModels.cs
index bcf7e5b7..4e5d2a3d 100644
--- a/FastReport.Core.Web/Services/ServicesParamsModels.cs
+++ b/FastReport.Core.Web/Services/ServicesParamsModels.cs
@@ -44,6 +44,22 @@ public sealed class CustomViewModel
public string SqlQuery { get; set; }
}
+ public class UpdateTableParams
+ {
+ public string ConnectionString { get; set; }
+ public string SqlQuery { get; set; }
+ public List Parameters { get; set; }
+ public string TableName { get; set; }
+ }
+
+ public sealed class ParameterModel
+ {
+ public string Name { get; set; }
+ public int DataType { get; set; }
+ public string Value { get; set; }
+ public string Expression { get; set; }
+ }
+
#region GetReportServiceParams
public class GetReportServiceParams
{
diff --git a/FastReport.OpenSource/Engine/ReportEngine.Dialogs.OpenSource.cs b/FastReport.OpenSource/Engine/ReportEngine.Dialogs.OpenSource.cs
index aa0336e5..cd4019e8 100644
--- a/FastReport.OpenSource/Engine/ReportEngine.Dialogs.OpenSource.cs
+++ b/FastReport.OpenSource/Engine/ReportEngine.Dialogs.OpenSource.cs
@@ -1,4 +1,5 @@
using FastReport.Dialog;
+using System.Threading.Tasks;
using System.Windows.Forms;
namespace FastReport.Engine
@@ -13,6 +14,11 @@ private bool RunDialogs()
return true;
}
+ private Task RunDialogsAsync()
+ {
+ return Task.FromResult(true);
+ }
+
#endregion Private Methods
}
}
diff --git a/FastReport/Resources/en.xml b/FastReport/Resources/en.xml
index 013fb170..8996c7df 100644
--- a/FastReport/Resources/en.xml
+++ b/FastReport/Resources/en.xml
@@ -796,7 +796,8 @@
-
+
+
@@ -1846,6 +1847,8 @@
+
+
@@ -2862,8 +2865,8 @@
-
-
+
+
@@ -2888,19 +2891,21 @@
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Arabic.frl b/Localization/Arabic.frl
index 2cf79cee..abd20da9 100644
--- a/Localization/Arabic.frl
+++ b/Localization/Arabic.frl
@@ -1960,8 +1960,8 @@
-
-
+
+
@@ -1972,11 +1972,11 @@
-
-
+
+
-
-
+
+
diff --git a/Localization/Armenian.frl b/Localization/Armenian.frl
index 7e5b37b8..5c4cabb1 100644
--- a/Localization/Armenian.frl
+++ b/Localization/Armenian.frl
@@ -1854,14 +1854,14 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Croatian.frl b/Localization/Croatian.frl
index 082ca508..bbdf4215 100644
--- a/Localization/Croatian.frl
+++ b/Localization/Croatian.frl
@@ -1475,11 +1475,11 @@
-
-
+
+
-
-
+
+
diff --git a/Localization/Czech.frl b/Localization/Czech.frl
index 5eab842a..cd6f8ddb 100644
--- a/Localization/Czech.frl
+++ b/Localization/Czech.frl
@@ -1577,14 +1577,14 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Danish.frl b/Localization/Danish.frl
index 7566350b..cd9c769b 100644
--- a/Localization/Danish.frl
+++ b/Localization/Danish.frl
@@ -1488,11 +1488,11 @@
-
-
+
+
-
-
+
+
diff --git a/Localization/Dutch.frl b/Localization/Dutch.frl
index 7a136853..e0c26839 100644
--- a/Localization/Dutch.frl
+++ b/Localization/Dutch.frl
@@ -1488,11 +1488,11 @@
-
-
+
+
-
-
+
+
diff --git a/Localization/French.frl b/Localization/French.frl
index a626e369..8c2ff47e 100644
--- a/Localization/French.frl
+++ b/Localization/French.frl
@@ -1923,17 +1923,17 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/German.frl b/Localization/German.frl
index 1fe063dc..2b17ddb6 100644
--- a/Localization/German.frl
+++ b/Localization/German.frl
@@ -2734,8 +2734,8 @@
-
-
+
+
@@ -2765,11 +2765,11 @@
-
-
+
+
-
-
+
+
diff --git a/Localization/Greek.frl b/Localization/Greek.frl
index fef35c50..5295f289 100644
--- a/Localization/Greek.frl
+++ b/Localization/Greek.frl
@@ -2247,8 +2247,8 @@
-
-
+
+
@@ -2259,11 +2259,11 @@
-
-
+
+
-
-
+
+
diff --git a/Localization/Hungarian.frl b/Localization/Hungarian.frl
index 81b4506b..f4bc9e3a 100644
--- a/Localization/Hungarian.frl
+++ b/Localization/Hungarian.frl
@@ -1658,14 +1658,14 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Persian.frl b/Localization/Persian.frl
index 010a0860..dec0f271 100644
--- a/Localization/Persian.frl
+++ b/Localization/Persian.frl
@@ -1476,11 +1476,11 @@
-
-
+
+
-
-
+
+
diff --git a/Localization/Romanian.frl b/Localization/Romanian.frl
index 28e06866..002decd3 100644
--- a/Localization/Romanian.frl
+++ b/Localization/Romanian.frl
@@ -1498,14 +1498,14 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Russian.frl b/Localization/Russian.frl
index 02fd05ac..88d9dec6 100644
--- a/Localization/Russian.frl
+++ b/Localization/Russian.frl
@@ -1,4 +1,4 @@
-
+
@@ -197,8 +197,8 @@
-
+
@@ -794,7 +794,8 @@
-
+
+
@@ -1661,6 +1662,8 @@
+
+
@@ -1797,7 +1800,7 @@
-
+
@@ -1812,7 +1815,7 @@
-
+
@@ -2037,7 +2040,6 @@
-
@@ -2189,8 +2191,8 @@
-
+
@@ -2211,9 +2213,9 @@
-
-
-
+
+
+
@@ -2629,8 +2631,8 @@
-
-
+
+
@@ -2655,19 +2657,21 @@
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Slovak.frl b/Localization/Slovak.frl
index 0d315334..68b8c3a9 100644
--- a/Localization/Slovak.frl
+++ b/Localization/Slovak.frl
@@ -1490,14 +1490,14 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Slovenian.frl b/Localization/Slovenian.frl
index 4475d219..2ad7c03d 100644
--- a/Localization/Slovenian.frl
+++ b/Localization/Slovenian.frl
@@ -1878,14 +1878,14 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Spanish.frl b/Localization/Spanish.frl
index 9c0e4da0..bd0d4305 100644
--- a/Localization/Spanish.frl
+++ b/Localization/Spanish.frl
@@ -1760,14 +1760,14 @@
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/Localization/Thai.frl b/Localization/Thai.frl
index 48678c64..fd4fb550 100644
--- a/Localization/Thai.frl
+++ b/Localization/Thai.frl
@@ -1361,10 +1361,10 @@
-
-
-
-
+
+
+
+
diff --git a/Localization/Ukrainian.frl b/Localization/Ukrainian.frl
index 0e1ee4a5..fff51a9c 100644
--- a/Localization/Ukrainian.frl
+++ b/Localization/Ukrainian.frl
@@ -1487,11 +1487,11 @@
-
-
+
+
-
-
+
+
diff --git a/Pack/BuildScripts/Tasks/BaseTasks.cs b/Pack/BuildScripts/Tasks/BaseTasks.cs
index c66c909e..59eb62ad 100644
--- a/Pack/BuildScripts/Tasks/BaseTasks.cs
+++ b/Pack/BuildScripts/Tasks/BaseTasks.cs
@@ -205,4 +205,7 @@ internal string GetOutdir(string projectOrPath)
var additionDir = GetDirNameByProject(project);
return Path.Combine(outdir, additionDir);
}
+
+
+ partial void UseNugetLocalization(NuGetPackSettings settings, ProductType productType);
}
diff --git a/Pack/BuildScripts/Tasks/LocalizationPackage.cs b/Pack/BuildScripts/Tasks/LocalizationPackage.cs
index 6b93f98f..a7e99ca5 100644
--- a/Pack/BuildScripts/Tasks/LocalizationPackage.cs
+++ b/Pack/BuildScripts/Tasks/LocalizationPackage.cs
@@ -42,19 +42,21 @@ public void FastReportLocalization()
var nuGetPackSettings = new NuGetPackSettings
{
Id = projName,
- Authors = new[] { "Fast Reports Inc." },
- Owners = new[] { "Fast Reports Inc." },
- Description = "FastReport.Localization includes localization files for FastReport .NET, FastReport.Core, FastReport.WPF, FastReport.Mono and FastReport.OpenSource",
+ Authors = ["Fast Reports Inc."],
+ Owners = ["Fast Reports Inc."],
+ Description = "FastReport.Localization includes localization files for FastReport .NET, FastReport.Core, FastReport.WPF, FastReport.Avalonia, FastReport.Mono and FastReport.OpenSource",
ProjectUrl = new Uri("https://www.fast-report.com/en/product/fast-report-net"),
Icon = FRLOGO192PNG,
License = new NuSpecLicense { Type = "file", Value = MIT_LICENSE },
- Tags = new[] { "fastreport", "localization" , "frl"},
+ Tags = ["fastreport", "localization", "frl"],
Version = version,
BasePath = tempDir,
OutputDirectory = GetOutdir(projName),
Files = packFiles,
};
+ UseNugetLocalization(nuGetPackSettings, ProductType.Localization);
+
// pack
NuGetPack(nuGetPackSettings);
}
diff --git a/UsedPackages.version b/UsedPackages.version
index 10d0c7b4..d34fd136 100644
--- a/UsedPackages.version
+++ b/UsedPackages.version
@@ -4,14 +4,14 @@
- 2024.2.0
+ 2025.1.0
2.88.6
2.88.6
[11.0.6,)
- 2024.2.0
+ 2025.1.0
[4.0.1,)
[4.0.1,)
@@ -25,9 +25,9 @@
- 2024.2.0
+ 2025.1.0
- 2024.2.0
+ 2025.1.0