diff --git a/README.md b/README.md index 5b6ed4d8..f05055dc 100644 --- a/README.md +++ b/README.md @@ -529,6 +529,7 @@ Since 1.22.0, when value type is `byte[]` then system will save file path at cel #### 12. Merge same cells vertically This functionality is only supported in `xlsx` format and merges cells vertically between @merge and @endmerge tags. +You can use @mergelimit to limit boundaries of merging cells vertically. ```csharp var mergedFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); @@ -548,10 +549,18 @@ memoryStream.MergeSameCells(path); File content before and after merge: +Without merge limit: + Screenshot 2023-08-07 at 11 59 24 Screenshot 2023-08-07 at 11 59 57 +With merge limit: + +Screenshot 2023-08-08 at 18 21 00 + +Screenshot 2023-08-08 at 18 21 40 + #### 13. Skip null values New explicit option to write empty cells for null values: diff --git a/docs/README.md b/docs/README.md index d9af5553..cd36e1cb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,8 +22,9 @@ --- -### 1.31.1 -- [New] support automatic merge for same vertical cells between @merge and @endmerge tags (via @eynarhaji) +### 1.31.2 +- [New] Support automatic merge for same vertical cells between @merge and @endmerge tags (via @eynarhaji) +- [New] Limit merge tagged columns with @mergelimit column. First merge limited column and then merge other columns accordingly. (via @eynarhaji) ### 1.31.1 - [OPT] Support property cache #23 (via @RRQM_Home) diff --git a/samples/xlsx/TestMergeWithLimitTag.xlsx b/samples/xlsx/TestMergeWithLimitTag.xlsx new file mode 100644 index 00000000..855493f2 Binary files /dev/null and b/samples/xlsx/TestMergeWithLimitTag.xlsx differ diff --git a/src/MiniExcel/MiniExcelLibs.csproj b/src/MiniExcel/MiniExcelLibs.csproj index 9989f014..4cb83861 100644 --- a/src/MiniExcel/MiniExcelLibs.csproj +++ b/src/MiniExcel/MiniExcelLibs.csproj @@ -1,7 +1,7 @@  net45;netstandard2.0 - 1.31.1 + 1.31.2 MiniExcel diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs index 462c4483..6d03b610 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs @@ -199,12 +199,11 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo ColIndex = StringHelper.GetLetter(att), RowIndex = StringHelper.GetNumber(att) }; - }).ToList(); + }).OrderBy(x=>x.RowIndex).ToList(); - var mergeColumns = columns.Where(s => s.InnerText.Contains("@merge")).OrderBy(s => s.RowIndex) - .ToList(); - var endMergeColumns = columns.Where(s => s.InnerText.Contains("@endmerge")).OrderBy(s => s.RowIndex) - .ToList(); + var mergeColumns = columns.Where(s => s.InnerText.Contains("@merge")).ToList(); + var endMergeColumns = columns.Where(s => s.InnerText.Contains("@endmerge")).ToList(); + var mergeLimitColumn = mergeColumns.FirstOrDefault(x=>x.InnerText.Contains("@mergelimit")); foreach (var mergeColumn in mergeColumns) { @@ -227,8 +226,7 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo x.ColIndex == taggedColumn.Key.ColIndex && x.RowIndex > taggedColumn.Key.RowIndex && x.RowIndex < taggedColumn.Value.RowIndex)); } - - + Dictionary lastMergeCellIndexes = new Dictionary(); @@ -240,16 +238,29 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo foreach (var childNode in childNodes) { - var childNodeAtt = StringHelper.GetLetter(childNode.GetAttribute("r")); + var att = childNode.GetAttribute("r"); + var childNodeLetter = StringHelper.GetLetter(att); + var childNodeNumber = StringHelper.GetNumber(att); if(!string.IsNullOrEmpty(childNode.InnerText)) { var xmlNodes = calculatedColumns - .Where(j => j.InnerText == childNode.InnerText && j.ColIndex == childNodeAtt) + .Where(j => j.InnerText == childNode.InnerText && j.ColIndex == childNodeLetter) .OrderBy(s => s.RowIndex).ToList(); if (xmlNodes.Count > 1) { + if (mergeLimitColumn != null) + { + var limitedNode = calculatedColumns.First(j => + j.ColIndex == mergeLimitColumn.ColIndex && j.RowIndex == childNodeNumber); + + var limitedMaxNode = calculatedColumns.Last(j => + j.ColIndex == mergeLimitColumn.ColIndex && j.InnerText == limitedNode.InnerText); + + xmlNodes = xmlNodes.Where(j => j.RowIndex >= limitedNode.RowIndex && j.RowIndex <= limitedMaxNode.RowIndex).ToList(); + } + var firstRow = xmlNodes.FirstOrDefault(); var lastRow = xmlNodes.LastOrDefault(s => s.RowIndex <= firstRow?.RowIndex + xmlNodes.Count && @@ -280,7 +291,7 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo } } - childNode.SetAttribute("r", $"{childNodeAtt}{{{{$rowindex}}}}"); //TODO: + childNode.SetAttribute("r", $"{childNodeLetter}{{{{$rowindex}}}}"); //TODO: } } } diff --git a/tests/MiniExcelTests/MiniExcelTemplateTests.cs b/tests/MiniExcelTests/MiniExcelTemplateTests.cs index fb7fef0c..da514193 100644 --- a/tests/MiniExcelTests/MiniExcelTemplateTests.cs +++ b/tests/MiniExcelTests/MiniExcelTemplateTests.cs @@ -857,5 +857,22 @@ public void MergeSameCellsWithTagTest() Assert.Equal("A7:A8", mergedCells[2]); } } + + [Fact] + public void MergeSameCellsWithLimitTagTest() + { + var mergedFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + + var path = @"../../../../../samples/xlsx/TestMergeWithLimitTag.xlsx"; + + MiniExcel.MergeSameCells(mergedFilePath, path); + { + var mergedCells = Helpers.GetFirstSheetMergedCells(mergedFilePath); + + Assert.Equal("A3:A4", mergedCells[0]); + Assert.Equal("C3:C6", mergedCells[1]); + Assert.Equal("A5:A6", mergedCells[2]); + } + } } } diff --git a/tests/MiniExcelTests/MiniExcelTests.csproj b/tests/MiniExcelTests/MiniExcelTests.csproj index 587cb82d..44d607ab 100644 --- a/tests/MiniExcelTests/MiniExcelTests.csproj +++ b/tests/MiniExcelTests/MiniExcelTests.csproj @@ -1,7 +1,7 @@  - net5.0 + net5.0; false miniexcel.snk