diff --git a/ClosedXML.Report/ClosedXML.Report.csproj b/ClosedXML.Report/ClosedXML.Report.csproj
index 5c40cdc..386b4b4 100644
--- a/ClosedXML.Report/ClosedXML.Report.csproj
+++ b/ClosedXML.Report/ClosedXML.Report.csproj
@@ -4,20 +4,21 @@
netstandard2.0;netstandard2.1
10
ClosedXML.Report
- ClosedXML.Report
+ CustomEa.ClosedXML.Report
Debug;Release
https://github.com/ClosedXML/ClosedXML.Report
- https://github.com/ClosedXML/ClosedXML.Report
- Alexey Rozhkov, Alexey Pankratev
+ https://github.com/eagleoriginal/ClosedXML.Report
+ IOrlov, Alexey Rozhkov, Alexey Pankratev
MIT
- ClosedXML.Report
- See https://github.com/ClosedXML/ClosedXML.Report/releases/tag/$(productVersion)
- ClosedXML.Report is a tool for report generation and data analysis in .NET applications through the use of Microsoft Excel. ClosedXML.Report is a .NET-library for report generation Microsoft Excel without requiring Excel to be installed on the machine that's running the code.
+ CustomEa.ClosedXML.Report
+ See https://github.com/eagleoriginal/ClosedXML.Report/releases/tag/$(productVersion)
+ Not Intended for Common Use!!!
+Pacakge with Custom Changes in original ClosedXML.Report.
+ClosedXML.Report is a tool for report generation and data analysis in .NET applications through the use of Microsoft Excel. ClosedXML.Report is a .NET-library for report generation Microsoft Excel without requiring Excel to be installed on the machine that's running the code.
ClosedXML Reporting Excel
- ClosedXML
- https://github.com/ClosedXML/ClosedXML.Report/raw/develop/Resources/favicon-01.png
+ CustomEa.ClosedXML
MIT
- false
+ True
true
@@ -30,6 +31,8 @@
snupkg
true
ClosedXML.Report.snk
+ 0.2.13-Beta9
+ favicon-01.png
@@ -38,6 +41,13 @@
+
+ True
+ \
+
+
+
+
diff --git a/ClosedXML.Report/Excel/Subtotal.cs b/ClosedXML.Report/Excel/Subtotal.cs
index 76d0af5..7cf7ed4 100644
--- a/ClosedXML.Report/Excel/Subtotal.cs
+++ b/ClosedXML.Report/Excel/Subtotal.cs
@@ -351,8 +351,16 @@ private MoveData[] ScanRange(int groupBy)
var val = row.Cell(groupBy).GetString();
var isSummaryRow = row.IsSummary();
-
- if (string.IsNullOrEmpty(val) && !isSummaryRow)
+ var isLeftGroupEndLine = false;
+ if (false == isSummaryRow)
+ {
+ isLeftGroupEndLine = _groups.SingleOrDefault(gr => gr.Column == groupBy - 1 &&
+ gr.SummaryRow == null &&
+ gr.Range.RangeAddress.LastAddress.RowNumber == row.RowNumber()) != null;
+ }
+
+ if (this._summaryAbove && // TODO: Without _summaryAbove clarification all empty values fall into RangeType.HeaderRow category and as result no groups created
+ string.IsNullOrEmpty(val) && !isSummaryRow)
{
if (groupStart > 0)
{
@@ -364,6 +372,18 @@ private MoveData[] ScanRange(int groupBy)
continue;
}
+ if (isLeftGroupEndLine)
+ {
+ var localGroupStart = groupStart == 0
+ ? row.RangeAddress.Relative(_range.RangeAddress).FirstAddress.RowNumber
+ : groupStart;
+
+ groups.Add(CreateMoveTask(groupBy, prevVal, _range.Cell(localGroupStart, 1), row.LastCell(), RangeType.DataRange));
+ prevVal = null;
+ groupStart = 0;
+ continue;
+ }
+
if (val != prevVal)
{
if (groupStart > 0)
diff --git a/ClosedXML.Report/Excel/SubtotalSummaryFunc.cs b/ClosedXML.Report/Excel/SubtotalSummaryFunc.cs
index 8d1bd51..1487754 100644
--- a/ClosedXML.Report/Excel/SubtotalSummaryFunc.cs
+++ b/ClosedXML.Report/Excel/SubtotalSummaryFunc.cs
@@ -56,6 +56,8 @@ public virtual int FuncNum
}
}
+ public object DefaultValueForEmptySource { get; set; }
+
public Func GetCalculateDelegate;
internal object Calculate(IDataSource dataSource)
diff --git a/ClosedXML.Report/Excel/XlExtensions.cs b/ClosedXML.Report/Excel/XlExtensions.cs
index 368a9dd..cab6498 100644
--- a/ClosedXML.Report/Excel/XlExtensions.cs
+++ b/ClosedXML.Report/Excel/XlExtensions.cs
@@ -350,7 +350,13 @@ public static void ReplaceCFFormulaeToA1(this IXLWorksheet worksheet)
{
foreach (var format in worksheet.ConditionalFormats)
{
- var target = format.Ranges.OrderBy(x=>x.RangeAddress.FirstAddress.RowNumber)
+ format.Ranges.RemoveAll(range => false == range.RangeAddress.IsValid);
+ var validRanges = format.Ranges.Where(range => range.RangeAddress.IsValid).ToList();
+ if (false == validRanges.Any())
+ continue;
+
+ var target =
+ validRanges.OrderBy(x=>x.RangeAddress.FirstAddress.RowNumber)
.ThenBy(x=> x.RangeAddress.FirstAddress.ColumnNumber)
.First().FirstCell();
foreach (var v in format.Values.Where(v => v.Value.Value.StartsWith("&=")).ToList())
diff --git a/ClosedXML.Report/Options/GroupTag.cs b/ClosedXML.Report/Options/GroupTag.cs
index 5eb8bb6..8b61f89 100644
--- a/ClosedXML.Report/Options/GroupTag.cs
+++ b/ClosedXML.Report/Options/GroupTag.cs
@@ -12,6 +12,7 @@ OPTION PARAMS OBJECTS RNG Priority
"\PageBreaks"
"\TotalLabel"
"\GrandLabel"
+ "\DisableSubtotalLine"
"SummaryAbove" Range rD Normal
@@ -30,6 +31,8 @@ OPTION PARAMS OBJECTS RNG Priority
using ClosedXML.Excel;
using ClosedXML.Report.Excel;
using ClosedXML.Report.Utils;
+using DocumentFormat.OpenXml.Drawing;
+using DocumentFormat.OpenXml.Spreadsheet;
using MoreLinq;
namespace ClosedXML.Report.Options
@@ -40,6 +43,7 @@ public class GroupTag : SortTag
public bool PageBreaks => Parameters.ContainsKey("pagebreaks");
public bool DisableSubtotals => Parameters.ContainsKey("disablesubtotals");
+ public bool DisableSubtotalLine => Parameters.ContainsKey("disablesubtotalline");
public bool Collapse => Parameters.ContainsKey("collapse");
public bool DisableOutLine => Parameters.ContainsKey("disableoutline");
public bool OutLine => !Parameters.ContainsKey("disableoutline");
@@ -110,11 +114,48 @@ private void Process(ProcessingContext context, GroupTag[] groups, bool summaryA
var level = 0;
var rows = root.RowCount() - 1;
var columns = root.ColumnCount();
- if (rows <= 0 || columns <= 0)
+ if (columns <= 0)
+ {
return;
+ }
- var r = root.Offset(0, 0, rows, columns);
+ // Empty Total grand for report
+ if (rows <= 0)
+ {
+ if (disableGrandTotal)
+ return;
+
+ var r2= root.Offset(0, 0, 1, columns);
+ using (var subtotal = new Subtotal(r2, summaryAbove, groups, context.Evaluator))
+ {
+ if (TotalLabel != null) subtotal.TotalLabel = TotalLabel;
+ if (GrandLabel != null) subtotal.GrandLabel = GrandLabel;
+ if (!disableGrandTotal)
+ {
+ var total = subtotal.AddGrandTotal(summaries);
+ total.SummaryRow.Cell(2).Value = total.SummaryRow.Cell(1).Value;
+ total.SummaryRow.Cell(1).Value = Blank.Value;
+ level++;
+ }
+
+ foreach (var subGroup in subtotal.Groups.OrderBy(x => x.Column).Reverse())
+ {
+ FormatHeaderFooter(subGroup, groupRow);
+
+ GroupRender(subGroup, new GroupTag { Column = 1, Level = 1 });
+ }
+
+ r2.Rows().ForEach(r => r.WorksheetRow().OutlineLevel = 0);
+ }
+
+ // Rem DoDeleteSpecialRow
+ root.LastRow().Delete(XLShiftDeletedCells.ShiftCellsUp);
+ return;
+ }
+
+ var r = root.Offset(0, 0, rows, columns);
+
using (var subtotal = new Subtotal(r, summaryAbove, groups, context.Evaluator))
{
if (TotalLabel != null) subtotal.TotalLabel = TotalLabel;
@@ -129,9 +170,18 @@ private void Process(ProcessingContext context, GroupTag[] groups, bool summaryA
foreach (var g in groups.OrderBy(x => x.Column))
{
+ // Todo: New Feature Group Without Subtotal. Only Merge.
+ if (g.DisableSubtotalLine)
+ {
+ subtotal.ScanForGroups(g.Column);
+ g.Level = ++level;
+
+ continue;
+ }
+
Func labFormat = null;
if (!string.IsNullOrEmpty(g.LabelFormat))
- labFormat = title => string.Format(LabelFormat, title);
+ labFormat = title => string.Format(g.LabelFormat, title);
if (g.MergeLabels == MergeMode.Merge2 && summaries.Length == 0)
subtotal.ScanForGroups(g.Column);
@@ -218,7 +268,12 @@ protected virtual void GroupRender(SubtotalGroup subGroup, GroupTag grData)
var rng = subGroup.Range.Column(subGroup.Column);
if (subGroup.Range.RowCount() > 1)
{
- int cellIdx = _maxLevel - subGroup.Level + 1;
+ // TODO: Wrong Style apply for merged cells if on right has grouped total
+ // But in first cell i expect already cell with value and style
+ // Plus with DisableSubtotalLine feature this became totally wrong
+ int cellIdx = 1;
+ //int cellIdx = _maxLevel - subGroup.Level + 1; // TODO: Comment for future investigation
+
var style = rng.Cell(cellIdx).Style;
rng.Merge();
rng.Style = style;
diff --git a/ClosedXML.Report/Options/SummaryFuncTag.cs b/ClosedXML.Report/Options/SummaryFuncTag.cs
index 73e64ef..a852112 100644
--- a/ClosedXML.Report/Options/SummaryFuncTag.cs
+++ b/ClosedXML.Report/Options/SummaryFuncTag.cs
@@ -1,8 +1,11 @@
using System;
+using System.Linq;
using System.Linq.Expressions;
using ClosedXML.Excel;
using ClosedXML.Report.Excel;
using ClosedXML.Report.Utils;
+using DocumentFormat.OpenXml.Math;
+using DocumentFormat.OpenXml.Spreadsheet;
namespace ClosedXML.Report.Options
{
@@ -33,7 +36,24 @@ public override void Execute(ProcessingContext context)
calculatedRange = context.Range.Offset(0, summ.Column - 1, context.Range.RowCount() - 1, 1);
}
- if (summ.FuncNum == 0)
+ object[] items;
+ if (summ.DataSource != null)
+ {
+ items = summ.DataSource.GetAll();
+ }
+ else
+ {
+ items = (context.Value as IDataSource).GetAll();
+ }
+
+ if (items == null || items.Length == 0)
+ {
+ if (summ.DefaultValueForEmptySource != null)
+ {
+ summRow.Cell(summ.Column).Value = XLCellValueConverter.FromObject(summ.DefaultValueForEmptySource);
+ }
+ }
+ else if (summ.FuncNum == 0)
{
var value = summ.Calculate((IDataSource)context.Value);
summRow.Cell(summ.Column).Value = XLCellValueConverter.FromObject(value);
@@ -62,6 +82,14 @@ private SubtotalSummaryFunc GetFunc(ProcessingContext context)
//return XLDynamicExpressionParser.ParseLambda(new[] {par}, null, GetParameter("Over"));
};
func.DataSource = DataSource;
+
+ if (HasParameter("Default"))
+ {
+ var dlg = context.Evaluator.ParseExpression(GetParameter("Default"), new ParameterExpression[] {});
+
+ func.DefaultValueForEmptySource = dlg.DynamicInvoke();
+ }
+
return func;
}
}
diff --git a/ClosedXML.Report/RangeInterpreter.cs b/ClosedXML.Report/RangeInterpreter.cs
index 4c60b60..310757b 100644
--- a/ClosedXML.Report/RangeInterpreter.cs
+++ b/ClosedXML.Report/RangeInterpreter.cs
@@ -178,6 +178,13 @@ string EvalString(string str)
{
var grownRange = rng.GrowToMergedRanges();
var items = nr.RangeData as object[] ?? nr.RangeData.Cast