diff --git a/src/EPPlus/Core/Worksheet/WorksheetCopyHelper.cs b/src/EPPlus/Core/Worksheet/WorksheetCopyHelper.cs index 9949fb8a3..8c4c9ad29 100644 --- a/src/EPPlus/Core/Worksheet/WorksheetCopyHelper.cs +++ b/src/EPPlus/Core/Worksheet/WorksheetCopyHelper.cs @@ -1001,7 +1001,7 @@ private static bool HasExternalReference(string formula) { if(formula!=null && formula.IndexOf('[') >= 0) { - var t=SourceCodeTokenizer.Default.Tokenize(formula); + var t=SourceCodeTokenizer.Default.Tokenize(formula); return t.Any(x => x.TokenType == TokenType.ExternalReference); } return false; diff --git a/src/EPPlus/DataValidation/ExcelDatavalidationAddress.cs b/src/EPPlus/DataValidation/ExcelDatavalidationAddress.cs index ecd34940a..d622521b1 100644 --- a/src/EPPlus/DataValidation/ExcelDatavalidationAddress.cs +++ b/src/EPPlus/DataValidation/ExcelDatavalidationAddress.cs @@ -25,7 +25,7 @@ internal ExcelDatavalidationAddress(string address, ExcelDataValidation val) : b /// /// Called before the address changes /// - internal protected override void BeforeChangeAddress() + internal protected override void BeforeChangeAddress(string value) { addressBeforeChange = _val.Address; _val._ws.DataValidations.ClearRangeDictionary(_val.Address); @@ -34,7 +34,7 @@ internal protected override void BeforeChangeAddress() /// /// Called when the address changes /// - internal protected override void ChangeAddress() + internal protected override void ChangeAddress(string value) { _val._ws.DataValidations.dvQuadTree.UpdateAddress(addressBeforeChange, _val.Address, _val); _val._ws.DataValidations.AddToRangeDictionary(_val); diff --git a/src/EPPlus/ExcelAddress.cs b/src/EPPlus/ExcelAddress.cs index 421a0fbdf..48dc7352a 100644 --- a/src/EPPlus/ExcelAddress.cs +++ b/src/EPPlus/ExcelAddress.cs @@ -104,10 +104,10 @@ public ExcelAddress(string Address, ExcelPackage package, ExcelAddressBase refer } set { - BeforeChangeAddress(); + BeforeChangeAddress(value); SetAddress(value, null, null); - ChangeAddress(); + ChangeAddress(value); } - } + } } } diff --git a/src/EPPlus/ExcelAddressBase.cs b/src/EPPlus/ExcelAddressBase.cs index 841c1453f..e196bc689 100644 --- a/src/EPPlus/ExcelAddressBase.cs +++ b/src/EPPlus/ExcelAddressBase.cs @@ -466,17 +466,15 @@ internal ExcelAddressBase ToInternalAddress() /// /// Method for actions that must be taken before address is changed /// - internal protected virtual void BeforeChangeAddress() + internal protected virtual void BeforeChangeAddress(string value) { } /// /// Called when the address changes /// - internal protected virtual void ChangeAddress() + internal protected virtual void ChangeAddress(string value) { } - - private void SetWbWs(string address) { int pos; diff --git a/src/EPPlus/ExcelFormulaAddress.cs b/src/EPPlus/ExcelFormulaAddress.cs index 157bd9316..029822d39 100644 --- a/src/EPPlus/ExcelFormulaAddress.cs +++ b/src/EPPlus/ExcelFormulaAddress.cs @@ -122,7 +122,7 @@ private void GetFixed(string address, out bool rowFixed, out bool colFixed) set { SetAddress(value, null, null); - ChangeAddress(); + ChangeAddress(value); SetFixed(); } } diff --git a/src/EPPlus/ExcelNamedRange.cs b/src/EPPlus/ExcelNamedRange.cs index d7700d9f6..63dbd3b7f 100644 --- a/src/EPPlus/ExcelNamedRange.cs +++ b/src/EPPlus/ExcelNamedRange.cs @@ -11,16 +11,13 @@ Date Author Change 01/27/2020 EPPlus Software AB Initial release EPPlus 5 *************************************************************************************************/ using OfficeOpenXml.Core; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Information; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; using OfficeOpenXml.FormulaParsing.LexicalAnalysis; using OfficeOpenXml.FormulaParsing.Ranges; using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using OfficeOpenXml.Utils; using OfficeOpenXml.FormulaParsing; +using OfficeOpenXml.Utils.Formula; namespace OfficeOpenXml { @@ -142,11 +139,213 @@ internal object NameValue set; } IList _tokens = null; + /// + /// Set to true to validate and update formulas with cell references. + /// + public static bool ValidateCellAddressInFormulas = true; + private string _nameFormula; internal string NameFormula { - get; - set; - } + get + { + return _nameFormula; + } + set + { + if (value == null) + { + _nameFormula = value; + return; + } + _nameFormula = ValidateCellAddressInFormulas ? FormulaUtils.AddWorksheetReferenceToFormula(value, _worksheet, AllowRelativeAddress) : value; + } + } + /// + protected internal override void BeforeChangeAddress(string value) + { + if(!string.IsNullOrEmpty(value) && !value.Contains("!") && Worksheet == null) + { + throw new InvalidOperationException("Workbook name needs a worksheet in the address."); + } + + } + /// + protected internal override void ChangeAddress(string value) + { + + } + + /// + /// Set a range for this name. Will remove exsisting formula and value. + /// + /// + /// + public void SetRange(ExcelRangeBase range, bool allowRelativeAddress = false) + { + if (range.Worksheet != _worksheet) + { + throw new InvalidOperationException($"Cannot change range to another worksheet or set a range to a workbook name: {Name}. Either create a new name or move current name first."); + } + ResetObject(); + NameFormula = null; + NameValue = null; + + _workbook = range._workbook; + _worksheet = range._worksheet; + if (allowRelativeAddress) + { + Address = range.FullAddress; + } + else + { + Address = range.FullAddressAbsolute; + } + Value = range.Value; + } + + /// + /// Set a formula for this name. Will remove exsisting range and value. + /// + /// The formula for this name. + public void SetFormula(string formula) + { + ResetObject(); + NameFormula = formula; + this.Formula = NameFormula; + NameValue = null; + } + /// + /// Get a formula for this name. + /// + /// The formula for this name. + public string GetFormula() + { + return NameFormula; + } + + /// + /// Set a value for this name. Will remove exsisting range and formula. + /// + /// + public void SetValue(object value) + { + ResetObject(); + NameFormula = null; + NameValue = value; + Value = value; + } + /// + /// Get a value for this name. + /// + /// + public object GetValue() + { + return NameValue; + } + + /// + /// Move this defined name to a target worksheet. + /// + /// Worksheet to move this name to. + /// /// Optional new name for the defined name. + /// This name. + /// If this name does not contain a formula, value or range, this exception occurs. + public ExcelNamedRange Move(ExcelWorksheet worksheet, string name = null) + { + ExcelNamedRange enr = null; + name = name == null ? Name : name; + //Detect if formula, value or range + if (NameFormula != null) + enr = worksheet.Names.AddFormula(Name, NameFormula); + else if (NameValue != null) + enr = worksheet.Names.AddValue(Name, NameValue); + else if (LocalAddress != "#REF!") + enr = worksheet.Names.AddRange(Name, worksheet.Cells[Address]); + if (enr == null) throw new InvalidOperationException($"No value, formula or address has been set for this name: {Name}"); + if(_worksheet != null) + _worksheet.Names.Remove(Name); + else if(_workbook != null) + _workbook.Names.Remove(Name); + else + throw new InvalidOperationException($"No workbook or worksheet has been set for this name: {Name}"); + return enr; + } + /// + /// Move this defined name to target workbook. + /// + /// Workbook to move this name to. + /// Optional new name for the defined name. + /// This name. + /// If this name does not contain a formula, value or range, this exception occurs. + public ExcelNamedRange Move(ExcelWorkbook workbook, string name = null) + { + ExcelNamedRange enr = null; + name = name == null ? Name : name; + //Detect if formula, value or range + if (NameFormula != null) + enr = workbook.Names.AddFormula(Name, NameFormula); + else if (NameValue != null) + enr = workbook.Names.AddValue(Name, NameValue); + else if (LocalAddress != "#REF!") + enr = workbook.Names.AddRange(Name, _worksheet.Cells[Address]); + if(enr == null) throw new InvalidOperationException($"No value, formula or address has been set for this name: {Name}"); + if (_worksheet != null) + _worksheet.Names.Remove(Name); + else if (_workbook != null) + _workbook.Names.Remove(Name); + else + throw new InvalidOperationException($"No workbook or worksheet has been set for this name: {Name}"); + return enr; + } + /// + /// Creates a copy of the defined name to target worksheet. + /// + /// Worksheet to copy to. + /// The name for the copy. + /// A new ExcelNameRange + /// If the original does not contain a formula, value or range, this exception occurs. + public ExcelNamedRange Copy(ExcelWorksheet worksheet, string name) + { + //Detect if formula, value or range + if (NameFormula != null) + return worksheet.Names.AddFormula(name, NameFormula); + else if (NameValue != null) + return worksheet.Names.AddValue(name, NameValue); + else if (LocalAddress != "#REF!") + return worksheet.Names.AddRange(name, worksheet.Cells[Address]); + throw new InvalidOperationException($"No value, formula or address has been set for this name: {Name}"); + } + /// + /// Creates a copy of the defined name to target workbook. + /// + /// Workbook to copy to. + /// The name for the copy. + /// A new ExcelNameRange + /// If the original does not contain a formula, value or range, this exception occurs. + public ExcelNamedRange Copy(ExcelWorkbook workbook, string name) + { + //Detect if formula, value or range + if (NameFormula != null) + return workbook.Names.AddFormula(name, NameFormula); + else if (NameValue != null) + return workbook.Names.AddValue(name, NameValue); + else if (LocalAddress != "#REF!") + return workbook.Names.AddRange(name, _worksheet.Cells[Address]); + throw new InvalidOperationException($"No value, formula or address has been set for this name: {Name}"); + } + + private void ResetObject() + { + Init(Name, _sheet, Index, AllowRelativeAddress); + _address = Name; + _fromCol = -1; + _fromRow = -1; + _toCol = -1; + _toRow = -1; + _start = null; + _end = null; + } + string _r1c1Formula = ""; internal string GetRelativeFormula(int row, int col) { @@ -305,7 +504,7 @@ internal object GetValue(FormulaCellAddress currentCell) } else { - var values = NameValue as Dictionary; + var values = NameValue as Dictionary; if(values!=null) { if(values.ContainsKey(currentCell.CellId)) diff --git a/src/EPPlus/ExcelNamedRangeCollection.cs b/src/EPPlus/ExcelNamedRangeCollection.cs index 5e49c185a..e5094db59 100644 --- a/src/EPPlus/ExcelNamedRangeCollection.cs +++ b/src/EPPlus/ExcelNamedRangeCollection.cs @@ -16,6 +16,9 @@ Date Author Change using System.Collections; using System.Linq; using OfficeOpenXml.FormulaParsing.ExcelUtilities; +using OfficeOpenXml.FormulaParsing.LexicalAnalysis; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; +using OfficeOpenXml.Utils.Formula; namespace OfficeOpenXml { @@ -68,6 +71,38 @@ public ExcelNamedRange Add(string Name, ExcelRangeBase Range) return Add(Name, Range, false); } + /// + /// Adds a new named formula + /// + /// The name + /// The formula + /// + public ExcelNamedRange Add(string Name, string formula) + { + return AddFormula(Name, formula); + } + + /// + /// Adds a new value + /// + /// The name + /// The value + /// + public ExcelNamedRange Add(string Name, object value) + { + return AddValue(Name, value); + } + /// + /// Adds a new named range + /// + /// The name + /// The range + /// + /// + public ExcelNamedRange AddRange(string name, ExcelRangeBase range, bool allowRelativeAddress = false ) + { + return Add(name, range, allowRelativeAddress); + } /// /// Adds the name without validation as Excel allows some names on load that is not permitted in the GUI /// @@ -114,11 +149,10 @@ private void AddName(string Name, ExcelNamedRange item) /// public ExcelNamedRange AddValue(string Name, object value) { - if (!ExcelAddressUtil.IsValidName(Name)) - { - throw (new ArgumentException("Name contains invalid characters or is not valid.")); - } - + if (!ExcelAddressUtil.IsValidName(Name)) + { + throw (new ArgumentException("Name contains invalid characters or is not valid.")); + } var item = new ExcelNamedRange(Name,_wb, _ws, _dic.Count); item.NameValue = value; AddName(Name, item); @@ -132,24 +166,22 @@ public ExcelNamedRange AddValue(string Name, object value) /// /// public ExcelNamedRange AddFormula(string Name, string Formula) - { - if (!ExcelAddressUtil.IsValidName(Name)) - { - throw (new ArgumentException("Name contains invalid characters or is not valid.")); - } - - return AddFormulaNoValidation(Name, Formula); - } - - internal ExcelNamedRange AddFormulaNoValidation(string Name, string Formula) - { - var item = new ExcelNamedRange(Name, _wb, _ws, _dic.Count); - item.NameFormula = Formula; - AddName(Name, item); - return item; - } + { + if (!ExcelAddressUtil.IsValidName(Name)) + { + throw (new ArgumentException("Name contains invalid characters or is not valid.")); + } + return AddFormulaNoValidation(Name, Formula); + } - internal void Insert(int rowFrom, int colFrom, int rows, int cols, int lowerLimint = 0, int upperLimit = int.MaxValue) + internal ExcelNamedRange AddFormulaNoValidation(string Name, string Formula) + { + var item = new ExcelNamedRange(Name, _wb, _ws, _dic.Count); + item.NameFormula = Formula; + AddName(Name, item); + return item; + } + internal void Insert(int rowFrom, int colFrom, int rows, int cols, int lowerLimint = 0, int upperLimit = int.MaxValue) { Insert(rowFrom, colFrom, rows, cols, n => true, lowerLimint, upperLimit); } diff --git a/src/EPPlus/ExcelRange.cs b/src/EPPlus/ExcelRange.cs index 4ca131c49..f253b1d5c 100644 --- a/src/EPPlus/ExcelRange.cs +++ b/src/EPPlus/ExcelRange.cs @@ -67,7 +67,7 @@ public ExcelRange this[string Address] } } SetAddress(Address, _workbook, _worksheet.Name); - ChangeAddress(); + ChangeAddress(_address); } if((_fromRow < 1 || _fromCol < 1) && Address.Equals("#REF!", StringComparison.InvariantCultureIgnoreCase)==false) { @@ -118,7 +118,7 @@ private ExcelRange GetTableAddess(ExcelWorksheet _worksheet, string address) _end = null; _addresses = null; _address = GetAddress(_fromRow, _fromCol); - ChangeAddress(); + ChangeAddress(_address); return this; } } @@ -148,7 +148,7 @@ private ExcelRange GetTableAddess(ExcelWorksheet _worksheet, string address) _end = null; _addresses = null; _address = GetAddress(_fromRow, _fromCol, _toRow, _toCol); - ChangeAddress(); + ChangeAddress(_address); return this; } } diff --git a/src/EPPlus/ExcelRangeBase.cs b/src/EPPlus/ExcelRangeBase.cs index 1dd381478..089f3af3f 100644 --- a/src/EPPlus/ExcelRangeBase.cs +++ b/src/EPPlus/ExcelRangeBase.cs @@ -98,7 +98,7 @@ private void Init(ExcelWorksheet xlWorksheet) /// /// On change address handler /// - protected internal override void ChangeAddress() + protected internal override void ChangeAddress(string value) { if (Table != null) { diff --git a/src/EPPlus/FormulaParsing/LexicalAnalysis/Token.cs b/src/EPPlus/FormulaParsing/LexicalAnalysis/Token.cs index 0e47f7030..f1e1cdd2c 100644 --- a/src/EPPlus/FormulaParsing/LexicalAnalysis/Token.cs +++ b/src/EPPlus/FormulaParsing/LexicalAnalysis/Token.cs @@ -10,11 +10,6 @@ Date Author Change ************************************************************************************************* 01/27/2020 EPPlus Software AB Initial release EPPlus 5 *************************************************************************************************/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace OfficeOpenXml.FormulaParsing.LexicalAnalysis { /// @@ -23,7 +18,7 @@ namespace OfficeOpenXml.FormulaParsing.LexicalAnalysis public struct Token { const ushort IS_NEGATED=0x0001; - internal Token(TokenType tokenType) + internal Token(TokenType tokenType) { TokenType = tokenType; Value = null; diff --git a/src/EPPlus/Utils/Formula/FormulaUtils.cs b/src/EPPlus/Utils/Formula/FormulaUtils.cs new file mode 100644 index 000000000..2d2ddd872 --- /dev/null +++ b/src/EPPlus/Utils/Formula/FormulaUtils.cs @@ -0,0 +1,67 @@ +using OfficeOpenXml.FormulaParsing.Exceptions; +using OfficeOpenXml.FormulaParsing.LexicalAnalysis; +using System.Collections.Generic; +using System.Linq; + +namespace OfficeOpenXml.Utils.Formula +{ + internal static class FormulaUtils + { + /// + /// This method detects cell addresses and updates them so they have reference to the supplied worksheet. Example: SUM(A1 + B2) becomes SUM('Sheet 1'A$1$ + 'Sheet 1'!$B$2) + /// + /// Formula to check + /// Worksheet to add + /// If address is relative or absolute. Default is absolute. + /// A string containing the formual with worksheet reference on cell addresses. + /// If worksheet is null and a cell address is found this method will throw this exception. Setting worksheet to null could be usefull for checking validity of formulas on workbook. + internal static string AddWorksheetReferenceToFormula(string formula, ExcelWorksheet ws, bool allowRelativeAddress = false) + { + bool isWsNull = ws == null ? true : false; + var tokens = SourceCodeTokenizer.Default.Tokenize(formula); + Dictionary addresses = new Dictionary(); + List fixedTokens = new List(); + //Collect tokens + for (int i = 0; i < tokens.Count; i++) + { + if (tokens[i].TokenType == TokenType.CellAddress) + { + if (isWsNull) throw new InvalidFormulaException("Formula with cell address must have a worksheet."); + if (i == 0) + { + addresses.Add(i, tokens[i]); + } + else if (tokens[i - 1].TokenType != TokenType.WorksheetName) + { + addresses.Add(i, tokens[i]); + } + } + } + //if no cell address tokens found we can quit early and return the formula as is. + if (addresses.Count == 0) + { + return formula; + } + //Update tokens + for (int i = 0; i < tokens.Count; i++) + { + if (addresses.ContainsKey(i)) + { + string fullAddress = allowRelativeAddress ? ws.Cells[addresses[i].Value].FullAddress : ws.Cells[addresses[i].Value].FullAddressAbsolute; + var addressTokens = SourceCodeTokenizer.Default.Tokenize(fullAddress); + for (int j = 0; j < addressTokens.Count; j++) + { + fixedTokens.Add(addressTokens[j]); + } + } + else + { + fixedTokens.Add(tokens[i]); + } + } + //Create new formula string. + return string.Concat(fixedTokens.Select(t => t.Value)); + } + + } +} diff --git a/src/EPPlusTest/Core/ExternalReferenceTest.cs b/src/EPPlusTest/Core/ExternalReferenceTest.cs index 937da5c44..0320ca1cc 100644 --- a/src/EPPlusTest/Core/ExternalReferenceTest.cs +++ b/src/EPPlusTest/Core/ExternalReferenceTest.cs @@ -84,7 +84,7 @@ public void OpenAndCalculateExternalLinkFromCache() public void OpenAndCalculateExternalLinkFromPackage() { var p = OpenTemplatePackage("ExternalReferences\\ExtRef.xlsx"); - + ExcelNamedRange.ValidateCellAddressInFormulas = false; p.Workbook.ExternalLinks.Directories.Add(new DirectoryInfo(_testInputPathOptional)); p.Workbook.ExternalLinks.LoadWorkbooks(); p.Workbook.ExternalLinks[0].As.ExternalWorkbook.Package.Workbook.Calculate(); @@ -275,8 +275,8 @@ public void UpdateCacheShouldBeSameAsExcel() { var p = OpenTemplatePackage("ExternalReferences\\ExtRef.xlsx"); - - + + ExcelNamedRange.ValidateCellAddressInFormulas = false; var er = p.Workbook.ExternalLinks[0].As.ExternalWorkbook; var excelCache = GetExternalCache(er); @@ -312,6 +312,7 @@ public void UpdateCacheShouldBeSameAsExcel() public void AddExternalLinkShouldBeSameAsExcel() { var p = OpenPackage("AddedExtRef.xlsx", true); + ExcelNamedRange.ValidateCellAddressInFormulas = false; var ws1=CreateWorksheet1(p); var ws2 = p.Workbook.Worksheets.Add("Sheet2"); @@ -342,6 +343,7 @@ public void AddExternalLinkShouldBeSameAsExcel() public void AddExternalWorkbookNoUpdate() { var p = OpenPackage("AddedExtRefNoUpdate.xlsx", true); + ExcelNamedRange.ValidateCellAddressInFormulas = false; var ws1 = CreateWorksheet1(p); var ws2 = p.Workbook.Worksheets.Add("Sheet2"); @@ -368,6 +370,7 @@ public void AddExternalWorkbookNoUpdate() public void AddExternalWorkbookWithChartCache() { var p = OpenPackage("AddedExtRefChart.xlsx", true); + ExcelNamedRange.ValidateCellAddressInFormulas = false; var ws = p.Workbook.Worksheets.Add("SheetWithChart"); var er = p.Workbook.ExternalLinks.AddExternalWorkbook(new FileInfo(_testInputPath + "externalreferences\\FromWB1.xlsx")); diff --git a/src/EPPlusTest/Drawing/DrawingTest.cs b/src/EPPlusTest/Drawing/DrawingTest.cs index df52883bf..1742e9d77 100644 --- a/src/EPPlusTest/Drawing/DrawingTest.cs +++ b/src/EPPlusTest/Drawing/DrawingTest.cs @@ -713,7 +713,7 @@ public void DrawingWorksheetCopy() if (ws == null) Assert.Inconclusive("Shapes worksheet is missing"); var wsShapes = pck.Workbook.Worksheets.Add("Copy Shapes", ws); - Assert.AreEqual(188, wsShapes.Drawings.Count); + Assert.AreEqual(187, wsShapes.Drawings.Count); ws = pck.Workbook.Worksheets["Scatter"]; if (ws == null) Assert.Inconclusive("Scatter worksheet is missing"); diff --git a/src/EPPlusTest/WorkSheetTests.cs b/src/EPPlusTest/WorkSheetTests.cs index 72a7c4502..47048c347 100644 --- a/src/EPPlusTest/WorkSheetTests.cs +++ b/src/EPPlusTest/WorkSheetTests.cs @@ -42,7 +42,9 @@ Date Author Change using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using OfficeOpenXml.Utils.Formula; namespace EPPlusTest { @@ -867,6 +869,212 @@ public void WorksheetCopy() } } [TestMethod] + public void NameChangeValueToRange() + { + var p = new ExcelPackage(); + var wb = p.Workbook; + var ws = wb.Worksheets.Add("Sheet1"); + ws.Cells["C3"].Value = 3; + + var name = ws.Names.AddValue("Text", 9); + Assert.AreEqual(9, name.Value); + + name.SetRange(ws.Cells["C3"]); + Assert.AreEqual(3, name.Value); + + //SaveWorkbook("DefinedNames1315.xlsx", p); + } + [TestMethod] + public void NameChangeValueToFormula() + { + var p = new ExcelPackage(); + var wb = p.Workbook; + var ws = wb.Worksheets.Add("Sheet1"); + ws.Cells["C3"].Value = 3; + + var name = ws.Names.AddValue("Text", 9); + Assert.AreEqual(9, name.Value); + Assert.AreEqual(null, name.Formula); + + name.SetFormula("SUM(7+C3)"); + Assert.AreEqual(null, name.Value); + Assert.AreEqual("SUM(7+Sheet1!$C$3)", name.Formula); + + //SaveWorkbook("DefinedNames1315.xlsx", p); + } + [TestMethod] + public void NameChangeRangeToValue() + { + var p = new ExcelPackage(); + var wb = p.Workbook; + var ws = wb.Worksheets.Add("Sheet1"); + ws.Cells["C3"].Value = 3; + + var name = ws.Names.AddRange("Text", ws.Cells["C3"]); + Assert.AreEqual(3, name.Value); + + name.SetValue(9); + Assert.AreEqual(9, name.Value); + + //SaveWorkbook("DefinedNames1315.xlsx", p); + } + [TestMethod] + public void NameChangeRangeToFormula() + { + var p = new ExcelPackage(); + var wb = p.Workbook; + var ws = wb.Worksheets.Add("Sheet1"); + ws.Cells["C3"].Value = 3; + + var name = ws.Names.AddRange("Text", ws.Cells["C3"]); + Assert.AreEqual("", name.Formula); + + name.SetFormula("SUM(7+C3)"); + Assert.AreEqual("SUM(7+Sheet1!$C$3)", name.Formula); + + SaveWorkbook("DefinedNames1315.xlsx", p); + } + [TestMethod] + public void NameChangeFormulaToValue() + { + var p = new ExcelPackage(); + var wb = p.Workbook; + var ws = wb.Worksheets.Add("Sheet1"); + ws.Cells["C3"].Value = 3; + + var name = ws.Names.AddFormula("Text", "SUM(7+C3)"); + Assert.AreEqual(null, name.Value); + Assert.AreEqual("SUM(7+Sheet1!$C$3)", name.Formula); + + name.SetValue(9); + Assert.AreEqual(9, name.Value); + Assert.AreEqual(null, name.Formula); + //SaveWorkbook("DefinedNames1315.xlsx", p); + } + [TestMethod] + public void NameChangeFormulaToRange() + { + var p = new ExcelPackage(); + var wb = p.Workbook; + var ws = wb.Worksheets.Add("Sheet1"); + ws.Cells["C3"].Value = 3; + + var name = ws.Names.AddFormula("Text", "SUM(7+C3)"); + Assert.AreEqual("SUM(7+Sheet1!$C$3)", name.Formula); + + name.SetRange(ws.Cells["C3"]); + Assert.AreEqual("", name.Formula); + Assert.AreEqual(3, name.Value); + + //SaveWorkbook("DefinedNames1315.xlsx", p); + } + + [TestMethod] + public void MoveNamesTest() + { + var p1 = new ExcelPackage(); + var p2 = new ExcelPackage(); + + var wb1 = p1.Workbook; + var wb2 = p2.Workbook; + + var ws1a = p1.Workbook.Worksheets.Add("Sheet A"); + var ws1b = p1.Workbook.Worksheets.Add("Sheet B"); + var ws2 = p2.Workbook.Worksheets.Add("Sheet 1"); + + var AB = ws1a.Names.Add("MoveMeToB", ws1a.Cells["A1"]); + var WbA = wb1.Names.Add("MoveMeToA", 77); + var AWb = ws1a.Names.Add("MoveMeToWB", "SUM(45+45+45)"); + + //Move name from A To B + Assert.AreEqual(0, ws1b.Names.Count); + Assert.AreEqual(2, ws1a.Names.Count); + AB.Move(ws1b); + Assert.AreEqual(1, ws1b.Names.Count); + Assert.AreEqual(1, ws1a.Names.Count); + + //Move name from wb To A + Assert.AreEqual(1, ws1a.Names.Count); + Assert.AreEqual(1, wb1.Names.Count); + var moved = WbA.Move(ws1a); + Assert.AreEqual(2, ws1a.Names.Count); + Assert.AreEqual(0, wb1.Names.Count); + + //Move name from A to wb + Assert.AreEqual(0, wb1.Names.Count); + AWb.Move(wb1); + Assert.AreEqual(1, wb1.Names.Count); + + //Move name from A to wb2 + Assert.AreEqual(0, wb2.Names.Count); + moved.Move(wb2); + Assert.AreEqual(1, wb2.Names.Count); + + //SaveWorkbook("DefinedNamesCopyP1.xlsx", p1); + //SaveWorkbook("DefinedNamesCopyP2.xlsx", p2); + } + [TestMethod] + public void CopyNamesTest() + { + var p1 = new ExcelPackage(); + var p2 = new ExcelPackage(); + + var wb1 = p1.Workbook; + var wb2 = p2.Workbook; + + var ws1a = p1.Workbook.Worksheets.Add("Sheet A"); + var ws1b = p1.Workbook.Worksheets.Add("Sheet B"); + var ws2 = p2.Workbook.Worksheets.Add("Sheet 1"); + + var AB = ws1a.Names.Add("CopyMeToB", ws1a.Cells["A1"]); + var WbA = wb1.Names.Add("CopyMeToA", 77); + var AWb = ws1a.Names.Add("CopyMeToWB", "SUM(45+45+45)"); + + //Copy name from A To B + Assert.AreEqual(0, ws1b.Names.Count); + AB.Copy(ws1b, "FromA"); + Assert.AreEqual(1, ws1b.Names.Count); + + //Copy name from wb To A + Assert.AreEqual(2, ws1a.Names.Count); + WbA.Copy(ws1a, "FromWb"); + Assert.AreEqual(3, ws1a.Names.Count); + + //Copy name from A to wb + Assert.AreEqual(1, wb1.Names.Count); + AWb.Copy(wb1, "FromA"); + Assert.AreEqual(2, wb1.Names.Count); + + //Copy name from A to wb2 + Assert.AreEqual(0, wb2.Names.Count); + AWb.Copy(wb2, "FromA"); + Assert.AreEqual(1, wb2.Names.Count); + + //SaveWorkbook("DefinedNamesCopyP1.xlsx", p1); + //SaveWorkbook("DefinedNamesCopyP2.xlsx", p2); + } + + [TestMethod] + public void AddWorksheetReferenceToFormulaTests() + { + var p = new ExcelPackage(); + var ws = p.Workbook.Worksheets.Add("Sheet 1"); + + var f1 = FormulaUtils.AddWorksheetReferenceToFormula("SUM(B2+3)", ws); + var f2 = FormulaUtils.AddWorksheetReferenceToFormula("SUM(B2+AS123+3+D20)", ws); + var f3 = FormulaUtils.AddWorksheetReferenceToFormula("SUM('Sheet 1'!$B$2+3)", ws); + var f4 = FormulaUtils.AddWorksheetReferenceToFormula("SUM(3+2)", ws); + var f5 = FormulaUtils.AddWorksheetReferenceToFormula("A2*SUM(3+2)", ws); + var f6 = FormulaUtils.AddWorksheetReferenceToFormula("SUM(B2+3)", ws, true); + + Assert.AreEqual("SUM('Sheet 1'!$B$2+3)", f1); + Assert.AreEqual("SUM('Sheet 1'!$B$2+'Sheet 1'!$AS$123+3+'Sheet 1'!$D$20)", f2); + Assert.AreEqual("SUM('Sheet 1'!$B$2+3)", f3); + Assert.AreEqual("SUM(3+2)", f4); + Assert.AreEqual("'Sheet 1'!$A$2*SUM(3+2)", f5); + Assert.AreEqual("SUM('Sheet 1'!B2+3)", f6); + } + public void CopyWorksheetDefinedNames() { using var p = OpenTemplatePackage("CopyWorksheetNames.xlsx"); @@ -916,9 +1124,8 @@ public void CopyWorksheetDefinedNamesEpplusOnly() var ws2 = wb2.Worksheets.Add("CopyWs", ws); var ws3 = wb2.Worksheets.Add("CopyWs2", ws); - //To be fixed in PR 1999 - Assert.AreEqual(5,wb2.Names.Count()); - Assert.AreEqual(wb2.Names[2].Value, wb.Names[2].Value); + Assert.AreEqual(1 ,wb2.Names.Count()); + Assert.AreEqual(wb2.Names.ContainsKey("AWorkbookRange"), wb.Names.ContainsKey("AWorkbookRange")); SaveAndCleanup(p2); } SaveAndCleanup(p);