diff --git a/ClosedXML/Excel/Tables/IXLTable.cs b/ClosedXML/Excel/Tables/IXLTable.cs
index 0f714ef..e6c2912 100644
--- a/ClosedXML/Excel/Tables/IXLTable.cs
+++ b/ClosedXML/Excel/Tables/IXLTable.cs
@@ -36,73 +36,85 @@
/// Appends the IEnumerable data elements and returns the range of the new rows.
///
/// The IEnumerable data.
+ /// if set to true propagate extra columns' values and formulas.
///
/// The range of the new rows.
///
- IXLRange AppendData(IEnumerable data);
+ IXLRange AppendData(IEnumerable data, Boolean propagateExtraColumns = false);
///
/// Appends the IEnumerable data elements and returns the range of the new rows.
///
/// The IEnumerable data.
/// if set to true the data will be transposed before inserting.
+ /// if set to true propagate extra columns' values and formulas.
///
/// The range of the new rows.
///
- IXLRange AppendData(IEnumerable data, Boolean transpose);
+ IXLRange AppendData(IEnumerable data, Boolean transpose, Boolean propagateExtraColumns = false);
///
/// Appends the data of a data table and returns the range of the new rows.
///
/// The data table.
+ /// if set to true propagate extra columns' values and formulas.
///
/// The range of the new rows.
///
- IXLRange AppendData(DataTable dataTable);
+ IXLRange AppendData(DataTable dataTable, Boolean propagateExtraColumns = false);
///
/// Appends the IEnumerable data elements and returns the range of the new rows.
///
///
/// The table data.
+ /// if set to true propagate extra columns' values and formulas.
///
/// The range of the new rows.
///
- IXLRange AppendData(IEnumerable data);
+ IXLRange AppendData(IEnumerable data, Boolean propagateExtraColumns = false);
///
/// Replaces the IEnumerable data elements and returns the table's data range.
///
/// The IEnumerable data.
- /// The table's data range.
- IXLRange ReplaceData(IEnumerable data);
+ /// if set to true propagate extra columns' values and formulas.
+ ///
+ /// The table's data range.
+ ///
+ IXLRange ReplaceData(IEnumerable data, Boolean propagateExtraColumns = false);
///
/// Replaces the IEnumerable data elements and returns the table's data range.
///
/// The IEnumerable data.
/// if set to true the data will be transposed before inserting.
+ /// if set to true propagate extra columns' values and formulas.
///
/// The table's data range.
///
- IXLRange ReplaceData(IEnumerable data, Boolean transpose);
+ IXLRange ReplaceData(IEnumerable data, Boolean transpose, Boolean propagateExtraColumns = false);
///
/// Replaces the data from the records of a data table and returns the table's data range.
///
/// The data table.
+ /// if set to true propagate extra columns' values and formulas.
///
/// The table's data range.
///
- IXLRange ReplaceData(DataTable dataTable);
+ IXLRange ReplaceData(DataTable dataTable, Boolean propagateExtraColumns = false);
///
/// Replaces the IEnumerable data elements as a table and the table's data range.
///
///
/// The table data.
- /// The table's data range.
- IXLRange ReplaceData(IEnumerable data);
+ /// if set to true propagate extra columns' values and formulas.
+ ///
+ /// The table's data range.
+ ///
+ IXLRange ReplaceData(IEnumerable data, Boolean propagateExtraColumns = false);
///
/// Resizes the table to the specified range address.
diff --git a/ClosedXML/Excel/Tables/XLTable.cs b/ClosedXML/Excel/Tables/XLTable.cs
index aa0024d..1739063 100644
--- a/ClosedXML/Excel/Tables/XLTable.cs
+++ b/ClosedXML/Excel/Tables/XLTable.cs
@@ -872,12 +872,12 @@
#region Append and replace data
- public IXLRange AppendData(IEnumerable data)
+ public IXLRange AppendData(IEnumerable data, Boolean propagateExtraColumns = false)
{
- return AppendData(data, false);
+ return AppendData(data, transpose: false, propagateExtraColumns: propagateExtraColumns);
}
- public IXLRange AppendData(IEnumerable data, bool transpose)
+ public IXLRange AppendData(IEnumerable data, bool transpose, Boolean propagateExtraColumns = false)
{
var castedData = data?.Cast();
if (!(castedData?.Any() ?? false) || data is String)
@@ -887,16 +887,21 @@
var lastRowOfOldRange = this.DataRange.LastRow();
lastRowOfOldRange.InsertRowsBelow(numberOfNewRows);
+ this.Fields.Cast().ForEach(f => f.Column = null);
- return lastRowOfOldRange.RowBelow().FirstCell().InsertData(castedData, transpose);
+ var insertedRange = lastRowOfOldRange.RowBelow().FirstCell().InsertData(castedData, transpose);
+
+ PropagateExtraColumns(insertedRange.ColumnCount(), lastRowOfOldRange.RowNumber());
+
+ return insertedRange;
}
- public IXLRange AppendData(DataTable dataTable)
+ public IXLRange AppendData(DataTable dataTable, Boolean propagateExtraColumns = false)
{
- return AppendData(dataTable.Rows.Cast());
+ return AppendData(dataTable.Rows.Cast(), propagateExtraColumns: propagateExtraColumns);
}
- public IXLRange AppendData(IEnumerable data)
+ public IXLRange AppendData(IEnumerable data, Boolean propagateExtraColumns = false)
{
if (!(data?.Any() ?? false) || data is String)
return null;
@@ -908,16 +913,21 @@
var lastRowOfOldRange = this.DataRange.LastRow();
lastRowOfOldRange.InsertRowsBelow(numberOfNewRows);
+ this.Fields.Cast().ForEach(f => f.Column = null);
- return lastRowOfOldRange.RowBelow().FirstCell().InsertData(data);
+ var insertedRange = lastRowOfOldRange.RowBelow().FirstCell().InsertData(data);
+
+ PropagateExtraColumns(insertedRange.ColumnCount(), lastRowOfOldRange.RowNumber());
+
+ return insertedRange;
}
- public IXLRange ReplaceData(IEnumerable data)
+ public IXLRange ReplaceData(IEnumerable data, Boolean propagateExtraColumns = false)
{
- return ReplaceData(data, false);
+ return ReplaceData(data, transpose: false, propagateExtraColumns: propagateExtraColumns);
}
- public IXLRange ReplaceData(IEnumerable data, bool transpose)
+ public IXLRange ReplaceData(IEnumerable data, bool transpose, Boolean propagateExtraColumns = false)
{
var castedData = data?.Cast();
if (!(castedData?.Any() ?? false) || data is String)
@@ -931,6 +941,7 @@
if (sizeDifference > 0)
this.DataRange.LastRow().InsertRowsBelow(sizeDifference);
else if (sizeDifference < 0)
+ {
this.DataRange.Rows
(
lastDataRowNumber + sizeDifference + 1 - firstDataRowNumber + 1,
@@ -938,15 +949,28 @@
)
.Delete();
- return this.DataRange.FirstCell().InsertData(castedData, transpose);
+ // No propagation needed when reducing the number of rows
+ propagateExtraColumns = false;
+ }
+
+ if (sizeDifference != 0)
+ // Invalidate table fields' columns
+ this.Fields.Cast().ForEach(f => f.Column = null);
+
+ var replacedRange = this.DataRange.FirstCell().InsertData(castedData, transpose);
+
+ if (propagateExtraColumns)
+ PropagateExtraColumns(replacedRange.ColumnCount(), lastDataRowNumber);
+
+ return replacedRange;
}
- public IXLRange ReplaceData(DataTable dataTable)
+ public IXLRange ReplaceData(DataTable dataTable, Boolean propagateExtraColumns = false)
{
- return ReplaceData(dataTable.Rows.Cast());
+ return ReplaceData(dataTable.Rows.Cast(), propagateExtraColumns: propagateExtraColumns);
}
- public IXLRange ReplaceData(IEnumerable data)
+ public IXLRange ReplaceData(IEnumerable data, Boolean propagateExtraColumns = false)
{
if (!(data?.Any() ?? false) || data is String)
throw new InvalidOperationException("Cannot replace table data with empty enumerable.");
@@ -959,6 +983,7 @@
if (sizeDifference > 0)
this.DataRange.LastRow().InsertRowsBelow(sizeDifference);
else if (sizeDifference < 0)
+ {
this.DataRange.Rows
(
lastDataRowNumber + sizeDifference + 1 - firstDataRowNumber + 1,
@@ -966,7 +991,38 @@
)
.Delete();
- return this.DataRange.FirstCell().InsertData(data);
+ // No propagation needed when reducing the number of rows
+ propagateExtraColumns = false;
+ }
+
+ if (sizeDifference != 0)
+ // Invalidate table fields' columns
+ this.Fields.Cast().ForEach(f => f.Column = null);
+
+ var replacedRange = this.DataRange.FirstCell().InsertData(data);
+
+ if (propagateExtraColumns)
+ PropagateExtraColumns(replacedRange.ColumnCount(), lastDataRowNumber);
+
+ return replacedRange;
+ }
+
+ private void PropagateExtraColumns(int numberOfNonExtraColumns, int previousLastDataRow)
+ {
+ for (var i = numberOfNonExtraColumns; i < this.Fields.Count(); i++)
+ {
+ var field = this.Field(i);
+
+ var cell = this.Worksheet.Cell(previousLastDataRow, field.Column.ColumnNumber());
+ field.Column.Cells(c => c.Address.RowNumber > previousLastDataRow)
+ .ForEach(c =>
+ {
+ if (cell.HasFormula)
+ c.FormulaR1C1 = cell.FormulaR1C1;
+ else
+ c.Value = cell.Value;
+ });
+ }
}
#endregion Append and replace data
diff --git a/ClosedXML_Tests/Excel/Tables/AddingAndReplacingTableDataTests.cs b/ClosedXML_Tests/Excel/Tables/AddingAndReplacingTableDataTests.cs
index 5dbd18d..ccae4ad 100644
--- a/ClosedXML_Tests/Excel/Tables/AddingAndReplacingTableDataTests.cs
+++ b/ClosedXML_Tests/Excel/Tables/AddingAndReplacingTableDataTests.cs
@@ -57,6 +57,26 @@
return wb;
}
+ private XLWorkbook PrepareWorkbookWithAdditionalColumns()
+ {
+ var wb = PrepareWorkbook();
+ var ws = wb.Worksheets.First();
+
+ var table = ws.Tables.First();
+ table.HeadersRow()
+ .LastCell().CellRight()
+ .InsertData(new[] { "CumulativeAge", "NameLength", "IsOld", "HardCodedValue" }, transpose: true);
+
+ table.Resize(ws.Range(table.FirstCell(), table.LastCell().CellRight(4)));
+
+ table.Field("CumulativeAge").DataCells.ForEach(c => c.FormulaA1 = $"SUM($G$3:G{c.WorksheetRow().RowNumber()})");
+ table.Field("NameLength").DataCells.ForEach(c => c.FormulaA1 = $"LEN(B{c.WorksheetRow().RowNumber()})");
+ table.Field("IsOld").DataCells.ForEach(c => c.FormulaA1 = $"=G{c.WorksheetRow().RowNumber()}>=40");
+ table.Field("HardCodedValue").DataCells.Value = "40 is not old!";
+
+ return wb;
+ }
+
private Person[] NewData
{
get
@@ -448,5 +468,208 @@
}
}
}
+
+ [Test]
+ public void CanReplaceWithUntypedEnumerableAndPropagateExtraColumns()
+ {
+ using (var ms = new MemoryStream())
+ {
+ using (var wb = PrepareWorkbookWithAdditionalColumns())
+ {
+ var ws = wb.Worksheets.First();
+ var table = ws.Tables.First();
+
+ var list = new ArrayList();
+ list.AddRange(NewData);
+ list.AddRange(NewData);
+
+ var replacedRange = table.ReplaceData(list, propagateExtraColumns: true);
+
+ Assert.AreEqual("B3:G6", replacedRange.RangeAddress.ToString());
+
+ ws.Columns().AdjustToContents();
+
+ wb.SaveAs(ms);
+ }
+
+ using (var wb = new XLWorkbook(ms))
+ {
+ var table = wb.Worksheets.SelectMany(ws => ws.Tables).First();
+
+ Assert.AreEqual(4, table.DataRange.RowCount());
+ Assert.AreEqual(10, table.DataRange.ColumnCount());
+
+ Assert.AreEqual("SUM($G$3:G5)", table.Worksheet.Cell("H5").FormulaA1);
+ Assert.AreEqual("SUM($G$3:G6)", table.Worksheet.Cell("H6").FormulaA1);
+ Assert.AreEqual(100, table.Worksheet.Cell("H5").Value);
+ Assert.AreEqual(130, table.Worksheet.Cell("H6").Value);
+
+ Assert.AreEqual("LEN(B5)", table.Worksheet.Cell("I5").FormulaA1);
+ Assert.AreEqual("LEN(B6)", table.Worksheet.Cell("I6").FormulaA1);
+ Assert.AreEqual(16, table.Worksheet.Cell("I5").Value);
+ Assert.AreEqual(21, table.Worksheet.Cell("I6").Value);
+
+ Assert.AreEqual("G5>=40", table.Worksheet.Cell("J5").FormulaA1);
+ Assert.AreEqual("G6>=40", table.Worksheet.Cell("J6").FormulaA1);
+ Assert.AreEqual(false, table.Worksheet.Cell("J5").Value);
+ Assert.AreEqual(false, table.Worksheet.Cell("J6").Value);
+
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K5").Value);
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K6").Value);
+ }
+ }
+ }
+
+ [Test]
+ public void CanReplaceWithTypedEnumerableAndPropagateExtraColumns()
+ {
+ using (var ms = new MemoryStream())
+ {
+ using (var wb = PrepareWorkbookWithAdditionalColumns())
+ {
+ var ws = wb.Worksheets.First();
+
+ var table = ws.Tables.First();
+
+ IEnumerable personEnumerable = NewData.Concat(NewData).OrderBy(p => p.Age);
+ var replacedRange = table.ReplaceData(personEnumerable, propagateExtraColumns: true);
+
+ Assert.AreEqual("B3:G6", replacedRange.RangeAddress.ToString());
+ ws.Columns().AdjustToContents();
+
+ wb.SaveAs(ms);
+ }
+
+ using (var wb = new XLWorkbook(ms))
+ {
+ var table = wb.Worksheets.SelectMany(ws => ws.Tables).First();
+
+ Assert.AreEqual(4, table.DataRange.RowCount());
+ Assert.AreEqual(10, table.DataRange.ColumnCount());
+
+ Assert.AreEqual("SUM($G$3:G5)", table.Worksheet.Cell("H5").FormulaA1);
+ Assert.AreEqual("SUM($G$3:G6)", table.Worksheet.Cell("H6").FormulaA1);
+ Assert.AreEqual(95, table.Worksheet.Cell("H5").Value);
+ Assert.AreEqual(130, table.Worksheet.Cell("H6").Value);
+
+ Assert.AreEqual("LEN(B5)", table.Worksheet.Cell("I5").FormulaA1);
+ Assert.AreEqual("LEN(B6)", table.Worksheet.Cell("I6").FormulaA1);
+ Assert.AreEqual(16, table.Worksheet.Cell("I5").Value);
+ Assert.AreEqual(16, table.Worksheet.Cell("I6").Value);
+
+ Assert.AreEqual("G5>=40", table.Worksheet.Cell("J5").FormulaA1);
+ Assert.AreEqual("G6>=40", table.Worksheet.Cell("J6").FormulaA1);
+ Assert.AreEqual(false, table.Worksheet.Cell("J5").Value);
+ Assert.AreEqual(false, table.Worksheet.Cell("J6").Value);
+
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K5").Value);
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K6").Value);
+ }
+ }
+ }
+
+ [Test]
+ public void CanAppendWithUntypedEnumerableAndPropagateExtraColumns()
+ {
+ using (var ms = new MemoryStream())
+ {
+ using (var wb = PrepareWorkbookWithAdditionalColumns())
+ {
+ var ws = wb.Worksheets.First();
+ var table = ws.Tables.First();
+
+ var list = new ArrayList();
+ list.AddRange(NewData);
+ list.AddRange(NewData);
+
+ var appendedRange = table.AppendData(list, propagateExtraColumns: true);
+
+ Assert.AreEqual("B6:G9", appendedRange.RangeAddress.ToString());
+
+ ws.Columns().AdjustToContents();
+
+ wb.SaveAs(ms);
+ }
+
+ using (var wb = new XLWorkbook(ms))
+ {
+ var table = wb.Worksheets.SelectMany(ws => ws.Tables).First();
+
+ Assert.AreEqual(7, table.DataRange.RowCount());
+ Assert.AreEqual(10, table.DataRange.ColumnCount());
+
+ Assert.AreEqual("SUM($G$3:G8)", table.Worksheet.Cell("H8").FormulaA1);
+ Assert.AreEqual("SUM($G$3:G9)", table.Worksheet.Cell("H9").FormulaA1);
+ Assert.AreEqual(220, table.Worksheet.Cell("H8").Value);
+ Assert.AreEqual(250, table.Worksheet.Cell("H9").Value);
+
+ Assert.AreEqual("LEN(B8)", table.Worksheet.Cell("I8").FormulaA1);
+ Assert.AreEqual("LEN(B9)", table.Worksheet.Cell("I9").FormulaA1);
+ Assert.AreEqual(16, table.Worksheet.Cell("I8").Value);
+ Assert.AreEqual(21, table.Worksheet.Cell("I9").Value);
+
+ Assert.AreEqual("G8>=40", table.Worksheet.Cell("J8").FormulaA1);
+ Assert.AreEqual("G9>=40", table.Worksheet.Cell("J9").FormulaA1);
+ Assert.AreEqual(false, table.Worksheet.Cell("J8").Value);
+ Assert.AreEqual(false, table.Worksheet.Cell("J9").Value);
+
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K8").Value);
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K9").Value);
+ }
+ }
+ }
+
+ [Test]
+ public void CanAppendTypedEnumerableAndPropagateExtraColumns()
+ {
+ using (var ms = new MemoryStream())
+ {
+ using (var wb = PrepareWorkbookWithAdditionalColumns())
+ {
+ var ws = wb.Worksheets.First();
+
+ var table = ws.Tables.First();
+
+ IEnumerable personEnumerable =
+ NewData
+ .Concat(NewData)
+ .Concat(NewData)
+ .OrderBy(p => p.FirstName);
+
+ var addedRange = table.AppendData(personEnumerable);
+
+ Assert.AreEqual("B6:G11", addedRange.RangeAddress.ToString());
+ ws.Columns().AdjustToContents();
+
+ wb.SaveAs(ms);
+ }
+
+ using (var wb = new XLWorkbook(ms))
+ {
+ var table = wb.Worksheets.SelectMany(ws => ws.Tables).First();
+
+ Assert.AreEqual(9, table.DataRange.RowCount());
+ Assert.AreEqual(10, table.DataRange.ColumnCount());
+
+ Assert.AreEqual("SUM($G$3:G10)", table.Worksheet.Cell("H10").FormulaA1);
+ Assert.AreEqual("SUM($G$3:G11)", table.Worksheet.Cell("H11").FormulaA1);
+ Assert.AreEqual(280, table.Worksheet.Cell("H10").Value);
+ Assert.AreEqual(315, table.Worksheet.Cell("H11").Value);
+
+ Assert.AreEqual("LEN(B10)", table.Worksheet.Cell("I10").FormulaA1);
+ Assert.AreEqual("LEN(B11)", table.Worksheet.Cell("I11").FormulaA1);
+ Assert.AreEqual(16, table.Worksheet.Cell("I10").Value);
+ Assert.AreEqual(16, table.Worksheet.Cell("I11").Value);
+
+ Assert.AreEqual("G10>=40", table.Worksheet.Cell("J10").FormulaA1);
+ Assert.AreEqual("G11>=40", table.Worksheet.Cell("J11").FormulaA1);
+ Assert.AreEqual(false, table.Worksheet.Cell("J10").Value);
+ Assert.AreEqual(false, table.Worksheet.Cell("J11").Value);
+
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K10").Value);
+ Assert.AreEqual("40 is not old!", table.Worksheet.Cell("K11").Value);
+ }
+ }
+ }
}
}