diff --git a/ClosedXML/Excel/Cells/XLCell.cs b/ClosedXML/Excel/Cells/XLCell.cs index d667470..9b96722 100644 --- a/ClosedXML/Excel/Cells/XLCell.cs +++ b/ClosedXML/Excel/Cells/XLCell.cs @@ -966,8 +966,6 @@ set { _formulaR1C1 = XLHelper.IsNullOrWhiteSpace(value) ? null : value; - -// FormulaA1 = GetFormulaA1(value); } } @@ -1761,9 +1759,10 @@ { var matchString = match.Value; var matchIndex = match.Index; - if (value.Substring(0, matchIndex).CharCount('"') % 2 == 0) + if (value.Substring(0, matchIndex).CharCount('"') % 2 == 0 + && value.Substring(0, matchIndex).CharCount('\'') % 2 == 0) { -// Check if the match is in between quotes + // Check if the match is in between quotes sb.Append(value.Substring(lastIndex, matchIndex - lastIndex)); sb.Append(conversionType == FormulaConversionType.A1ToR1C1 ? GetR1C1Address(matchString, rowsToShift, columnsToShift) diff --git a/ClosedXML/Excel/Ranges/XLRangeBase.cs b/ClosedXML/Excel/Ranges/XLRangeBase.cs index 3484ed6..60d12d4 100644 --- a/ClosedXML/Excel/Ranges/XLRangeBase.cs +++ b/ClosedXML/Excel/Ranges/XLRangeBase.cs @@ -1388,7 +1388,7 @@ { var modelFirstColumn = model.FirstCellUsed(true); var modelLastColumn = model.LastCellUsed(true); - if (modelLastColumn != null) + if (modelFirstColumn != null && modelLastColumn != null) { Int32 firstCoReturned = modelFirstColumn.Address.ColumnNumber - model.RangeAddress.FirstAddress.ColumnNumber + 1; diff --git a/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs b/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs index 0f1c4ca..356424c 100644 --- a/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs +++ b/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs @@ -99,7 +99,7 @@ { _relIds.Add(relType, new List()); } - _relIds[relType].AddRange(values); + _relIds[relType].AddRange(values.Where(v => !_relIds[relType].Contains(v))); } public void Reset(RelType relType) { @@ -147,9 +147,5 @@ public IXLStyle Style; } #endregion - - - - } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/XLWorkbook_Save.cs b/ClosedXML/Excel/XLWorkbook_Save.cs index 8dd7f14..1a4f3bc 100644 --- a/ClosedXML/Excel/XLWorkbook_Save.cs +++ b/ClosedXML/Excel/XLWorkbook_Save.cs @@ -578,24 +578,36 @@ sheet.Name = wks.Name; } - foreach (var xlSheet in - WorksheetsInternal.Cast().Where(s => s.SheetId == 0).OrderBy(w => w.Position)) + foreach (var xlSheet in WorksheetsInternal.Cast().OrderBy(w => w.Position)) { - var rId = context.RelIdGenerator.GetNext(RelType.Workbook); - - while (WorksheetsInternal.Cast().Any(w => w.SheetId == Int32.Parse(rId.Substring(3)))) + string rId; + if (xlSheet.SheetId == 0) + { rId = context.RelIdGenerator.GetNext(RelType.Workbook); - xlSheet.SheetId = Int32.Parse(rId.Substring(3)); - xlSheet.RelId = rId; - var newSheet = new Sheet - { - Name = xlSheet.Name, - Id = rId, - SheetId = (UInt32)xlSheet.SheetId - }; + while (WorksheetsInternal.Cast().Any(w => w.SheetId == Int32.Parse(rId.Substring(3)))) + rId = context.RelIdGenerator.GetNext(RelType.Workbook); - workbook.Sheets.AppendChild(newSheet); + xlSheet.SheetId = Int32.Parse(rId.Substring(3)); + xlSheet.RelId = rId; + } + else + { + rId = String.Format("rId{0}", xlSheet.SheetId); + context.RelIdGenerator.AddValues(new List { rId }, RelType.Workbook); + } + + if (!workbook.Sheets.Cast().Any(s => s.Id == rId)) + { + var newSheet = new Sheet + { + Name = xlSheet.Name, + Id = rId, + SheetId = (UInt32)xlSheet.SheetId + }; + + workbook.Sheets.AppendChild(newSheet); + } } var sheetElements = from sheet in workbook.Sheets.Elements() diff --git a/ClosedXML/Extensions.cs b/ClosedXML/Extensions.cs index 1a33550..a37daf3 100644 --- a/ClosedXML/Extensions.cs +++ b/ClosedXML/Extensions.cs @@ -75,10 +75,10 @@ HashSet distinctItems = new HashSet(); foreach (var item in source) { - if (distinctItems.Contains(item)) - return true; - else - distinctItems.Add(item); + if (!distinctItems.Add(item)) + { + return true; + } } return false; } diff --git a/ClosedXML_Tests/ClosedXML_Tests.csproj b/ClosedXML_Tests/ClosedXML_Tests.csproj index f1c0f09..89a0eff 100644 --- a/ClosedXML_Tests/ClosedXML_Tests.csproj +++ b/ClosedXML_Tests/ClosedXML_Tests.csproj @@ -78,6 +78,7 @@ + @@ -249,11 +250,11 @@ + - @@ -265,4 +266,4 @@ --> - + \ No newline at end of file diff --git a/ClosedXML_Tests/Excel/Loading/LoadingTests.cs b/ClosedXML_Tests/Excel/Loading/LoadingTests.cs index 5412c0a..3568007 100644 --- a/ClosedXML_Tests/Excel/Loading/LoadingTests.cs +++ b/ClosedXML_Tests/Excel/Loading/LoadingTests.cs @@ -8,7 +8,7 @@ namespace ClosedXML_Tests.Excel { - // Tests in this fixture test only the successful loading of existing Excel files, + // Tests in this fixture test only the successful loading of existing Excel files, // i.e. we test that ClosedXML doesn't choke on a given input file // These tests DO NOT test that ClosedXML successfully recognises all the Excel parts or that it can successfully save those parts again. [TestFixture] @@ -19,7 +19,8 @@ { var files = new List() { - @"Misc\TableWithCustomTheme.xlsx" + @"Misc\TableWithCustomTheme.xlsx", + @"Misc\EmptyTable.xlsx" }; foreach (var file in files) @@ -27,5 +28,17 @@ TestHelper.LoadFile(file); } } + + [Test] + public void CanLoadAndManipulateFileWithEmptyTable() + { + using (var stream = TestHelper.GetStreamFromResource(TestHelper.GetResourcePath(@"Misc\EmptyTable.xlsx"))) + using (var wb = new XLWorkbook(stream)) + { + var ws = wb.Worksheets.First(); + var table = ws.Tables.First(); + table.DataRange.InsertRowsBelow(5); + } + } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/Excel/Misc/FormulaTests.cs b/ClosedXML_Tests/Excel/Misc/FormulaTests.cs index 9d7cc15..9e1e695 100644 --- a/ClosedXML_Tests/Excel/Misc/FormulaTests.cs +++ b/ClosedXML_Tests/Excel/Misc/FormulaTests.cs @@ -18,5 +18,25 @@ ws.Cell("A1").CopyTo("A2"); Assert.AreEqual("B2", ws.Cell("A2").FormulaA1); } + + [Test] + public void CopyFormulaWithSheetNameThatResemblesFormula() + { + using (var wb = new XLWorkbook()) + { + IXLWorksheet ws = wb.Worksheets.Add("S10 Data"); + ws.Cell("A1").Value = "Some value"; + + ws = wb.Worksheets.Add("Summary"); + ws.Cell("A1").FormulaA1 = "='S10 Data'!A1"; + Assert.AreEqual("Some value", ws.Cell("A1").Value); + + ws.Cell("A1").CopyTo("A2"); + Assert.AreEqual("'S10 Data'!A2", ws.Cell("A2").FormulaA1); + + ws.Cell("A1").CopyTo("B1"); + Assert.AreEqual("'S10 Data'!B1", ws.Cell("B1").FormulaA1); + } + } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/Excel/Saving/SavingTests.cs b/ClosedXML_Tests/Excel/Saving/SavingTests.cs new file mode 100644 index 0000000..22aa34a --- /dev/null +++ b/ClosedXML_Tests/Excel/Saving/SavingTests.cs @@ -0,0 +1,30 @@ +using ClosedXML.Excel; +using NUnit.Framework; +using System.IO; + +namespace ClosedXML_Tests.Excel.Saving +{ + [TestFixture] + public class SavingTests + { + [Test] + public void CanSuccessfullySaveFileMultipleTimes() + { + using (var wb = new XLWorkbook()) + { + var sheet = wb.Worksheets.Add("TestSheet"); + var memoryStream = new MemoryStream(); + wb.SaveAs(memoryStream, true); + + for (int i = 1; i <= 3; i++) + { + sheet.Cell(i, 1).Value = "test" + i; + wb.SaveAs(memoryStream, true); + } + + memoryStream.Close(); + memoryStream.Dispose(); + } + } + } +} diff --git a/ClosedXML_Tests/Resource/Misc/EmptyTable.xlsx b/ClosedXML_Tests/Resource/Misc/EmptyTable.xlsx new file mode 100644 index 0000000..e902376 --- /dev/null +++ b/ClosedXML_Tests/Resource/Misc/EmptyTable.xlsx Binary files differ diff --git a/ClosedXML_Tests/TestHelper.cs b/ClosedXML_Tests/TestHelper.cs index 07576df..36b5933 100644 --- a/ClosedXML_Tests/TestHelper.cs +++ b/ClosedXML_Tests/TestHelper.cs @@ -90,12 +90,20 @@ #pragma warning restore 162 } - public static void LoadFile(string filePartName) + public static string GetResourcePath(string filePartName) + { + return filePartName.Replace('\\', '.').TrimStart('.'); + } + + public static Stream GetStreamFromResource(string resourcePath) { var extractor = new ResourceFileExtractor(null, ".Resource."); + return extractor.ReadFileFromResToStream(resourcePath); + } - string resourcePath = filePartName.Replace('\\', '.').TrimStart('.'); - using (var stream = extractor.ReadFileFromResToStream(resourcePath)) + public static void LoadFile(string filePartName) + { + using (var stream = GetStreamFromResource(GetResourcePath(filePartName))) { var wb = new XLWorkbook(stream); wb.Dispose();