Establishes the foundational structure, core Excel functionality, and tooling. - Implements base components for cells, ranges, columns, and rows. - Integrates a custom calculation engine with a wide range of Excel functions. - Adds comprehensive support for styling, conditional formatting, data validation, comments, pictures, and charts. - Configures build setup, project metadata (NuGet), and developer guidelines. - Includes editor and Git attributes for consistent code style and line endings.
444 lines
18 KiB
C#
444 lines
18 KiB
C#
using ClosedXML.Excel;
|
|
using NUnit.Framework;
|
|
using System.Linq;
|
|
|
|
namespace ClosedXML_Tests
|
|
{
|
|
[TestFixture]
|
|
public class MergedRangesTests
|
|
{
|
|
[Test]
|
|
public void LastCellFromMerge()
|
|
{
|
|
var wb = new XLWorkbook();
|
|
IXLWorksheet ws = wb.Worksheets.Add("Sheet");
|
|
ws.Range("B2:D4").Merge();
|
|
|
|
string first = ws.FirstCellUsed(XLCellsUsedOptions.All).Address.ToStringRelative();
|
|
string last = ws.LastCellUsed(XLCellsUsedOptions.All).Address.ToStringRelative();
|
|
|
|
Assert.AreEqual("B2", first);
|
|
Assert.AreEqual("D4", last);
|
|
}
|
|
|
|
[TestCase("A1:A2", "A1:A2")]
|
|
[TestCase("A2:B2", "A2:B2")]
|
|
[TestCase("A3:C3", "A3:E3")]
|
|
[TestCase("B4:B6", "B4:B6")]
|
|
[TestCase("C7:D7", "E7:F7")]
|
|
public void MergedRangesShiftedOnColumnInsert(string originalRange, string expectedRange)
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
var range = ws.Range(originalRange).Merge();
|
|
|
|
ws.Column(2).InsertColumnsAfter(2);
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
Assert.AreEqual(1, mr.Length);
|
|
Assert.AreSame(range, mr.Single());
|
|
Assert.AreEqual(expectedRange, range.RangeAddress.ToString());
|
|
}
|
|
}
|
|
|
|
[TestCase("A1:B1", "A1:B1")]
|
|
[TestCase("B1:B2", "B1:B2")]
|
|
[TestCase("C1:C3", "C1:C5")]
|
|
[TestCase("D2:F2", "D2:F2")]
|
|
[TestCase("G4:G5", "G6:G7")]
|
|
public void MergedRangesShiftedOnRowInsert(string originalRange, string expectedRange)
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
var range = ws.Range(originalRange).Merge();
|
|
|
|
ws.Row(2).InsertRowsBelow(2);
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
Assert.AreEqual(1, mr.Length);
|
|
Assert.AreSame(range, mr.Single());
|
|
Assert.AreEqual(expectedRange, range.RangeAddress.ToString());
|
|
}
|
|
}
|
|
|
|
[TestCase("A1:A2", true, "A1:A2")]
|
|
[TestCase("A2:B2", true, "A2:A2")]
|
|
[TestCase("A3:C3", true, "A3:B3")]
|
|
[TestCase("B4:B6", false, "")]
|
|
[TestCase("C7:D7", true, "B7:C7")]
|
|
public void MergedRangesShiftedOnColumnDelete(string originalRange, bool expectedExist, string expectedRange)
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
var range = ws.Range(originalRange).Merge();
|
|
|
|
ws.Column(2).Delete();
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
if (expectedExist)
|
|
{
|
|
Assert.AreEqual(1, mr.Length);
|
|
Assert.AreSame(range, mr.Single());
|
|
Assert.AreEqual(expectedRange, range.RangeAddress.ToString());
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(0, mr.Length);
|
|
Assert.IsFalse(range.RangeAddress.IsValid);
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase("A1:B1", true, "A1:B1")]
|
|
[TestCase("B1:B2", true, "B1:B1")]
|
|
[TestCase("C1:C3", true, "C1:C2")]
|
|
[TestCase("D2:F2", false, "")]
|
|
[TestCase("G4:G5", true, "G3:G4")]
|
|
public void MergedRangesShiftedOnRowDelete(string originalRange, bool expectedExist, string expectedRange)
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
var range = ws.Range(originalRange).Merge();
|
|
|
|
ws.Row(2).Delete();
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
if (expectedExist)
|
|
{
|
|
Assert.AreEqual(1, mr.Length);
|
|
Assert.AreSame(range, mr.Single());
|
|
Assert.AreEqual(expectedRange, range.RangeAddress.ToString());
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(0, mr.Length);
|
|
Assert.IsFalse(range.RangeAddress.IsValid);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ShiftRangeRightBreaksMerges()
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
ws.Range("B2:C3").Merge();
|
|
ws.Range("B4:C5").Merge();
|
|
ws.Range("F2:G3").Merge(); // to be broken
|
|
ws.Range("F4:G5").Merge(); // to be broken
|
|
ws.Range("H1:I2").Merge();
|
|
ws.Range("H5:I6").Merge();
|
|
|
|
ws.Range("D3:E4").InsertColumnsAfter(2);
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
Assert.AreEqual(4, mr.Length);
|
|
Assert.AreEqual("H1:I2", mr[0].RangeAddress.ToString());
|
|
Assert.AreEqual("B2:C3", mr[1].RangeAddress.ToString());
|
|
Assert.AreEqual("B4:C5", mr[2].RangeAddress.ToString());
|
|
Assert.AreEqual("H5:I6", mr[3].RangeAddress.ToString());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ShiftRangeLeftBreaksMerges()
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
ws.Range("B2:C3").Merge();
|
|
ws.Range("B4:C5").Merge();
|
|
ws.Range("F2:G3").Merge(); // to be broken
|
|
ws.Range("F4:G5").Merge(); // to be broken
|
|
ws.Range("H1:I2").Merge();
|
|
ws.Range("H5:I6").Merge();
|
|
|
|
ws.Range("D3:E4").Delete(XLShiftDeletedCells.ShiftCellsLeft);
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
Assert.AreEqual(4, mr.Length);
|
|
Assert.AreEqual("H1:I2", mr[0].RangeAddress.ToString());
|
|
Assert.AreEqual("B2:C3", mr[1].RangeAddress.ToString());
|
|
Assert.AreEqual("B4:C5", mr[2].RangeAddress.ToString());
|
|
Assert.AreEqual("H5:I6", mr[3].RangeAddress.ToString());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void RangeShiftDownBreaksMerges()
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
ws.Range("B2:C3").Merge();
|
|
ws.Range("D2:E3").Merge();
|
|
ws.Range("B6:C7").Merge(); // to be broken
|
|
ws.Range("D6:E7").Merge(); // to be broken
|
|
ws.Range("A8:B9").Merge();
|
|
ws.Range("E8:F9").Merge();
|
|
|
|
ws.Range("C4:D5").InsertRowsBelow(2);
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
Assert.AreEqual(4, mr.Length);
|
|
Assert.AreEqual("B2:C3", mr[0].RangeAddress.ToString());
|
|
Assert.AreEqual("D2:E3", mr[1].RangeAddress.ToString());
|
|
Assert.AreEqual("A8:B9", mr[2].RangeAddress.ToString());
|
|
Assert.AreEqual("E8:F9", mr[3].RangeAddress.ToString());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void RangeShiftUpBreaksMerges()
|
|
{
|
|
using (var wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("MRShift");
|
|
ws.Range("B2:C3").Merge();
|
|
ws.Range("D2:E3").Merge();
|
|
ws.Range("B6:C7").Merge(); // to be broken
|
|
ws.Range("D6:E7").Merge(); // to be broken
|
|
ws.Range("A8:B9").Merge();
|
|
ws.Range("E8:F9").Merge();
|
|
|
|
ws.Range("C4:D5").Delete(XLShiftDeletedCells.ShiftCellsUp);
|
|
|
|
var mr = ws.MergedRanges.ToArray();
|
|
Assert.AreEqual(4, mr.Length);
|
|
Assert.AreEqual("B2:C3", mr[0].RangeAddress.ToString());
|
|
Assert.AreEqual("D2:E3", mr[1].RangeAddress.ToString());
|
|
Assert.AreEqual("A8:B9", mr[2].RangeAddress.ToString());
|
|
Assert.AreEqual("E8:F9", mr[3].RangeAddress.ToString());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MergedCellsAcquireFirstCellStyle()
|
|
{
|
|
using (XLWorkbook wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("Sheet1");
|
|
ws.Cell("A1").Style.Fill.BackgroundColor = XLColor.Red;
|
|
ws.Cell("A2").Style.Fill.BackgroundColor = XLColor.Yellow;
|
|
ws.Cell("A3").Style.Fill.BackgroundColor = XLColor.Green;
|
|
ws.Range("A1:A3").Merge();
|
|
|
|
Assert.AreEqual(XLColor.Red, ws.Cell("A1").Style.Fill.BackgroundColor);
|
|
Assert.AreEqual(XLColor.Red, ws.Cell("A2").Style.Fill.BackgroundColor);
|
|
Assert.AreEqual(XLColor.Red, ws.Cell("A3").Style.Fill.BackgroundColor);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MergedCellsLooseData()
|
|
{
|
|
using (XLWorkbook wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("Sheet1");
|
|
ws.Range("A1:A3").SetValue(100);
|
|
ws.Range("A1:A3").Merge();
|
|
|
|
Assert.AreEqual(100, ws.Cell("A1").Value);
|
|
Assert.AreEqual("", ws.Cell("A2").Value);
|
|
Assert.AreEqual("", ws.Cell("A3").Value);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MergedCellsLooseConditionalFormats()
|
|
{
|
|
using (XLWorkbook wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("Sheet1");
|
|
ws.Cell("A1").AddConditionalFormat().WhenContains("1").Fill.BackgroundColor = XLColor.Red;
|
|
ws.Cell("A2").AddConditionalFormat().WhenContains("2").Fill.BackgroundColor = XLColor.Yellow;
|
|
|
|
ws.Range("A1:A2").Merge();
|
|
|
|
Assert.AreEqual(1, ws.ConditionalFormats.Count());
|
|
Assert.AreEqual("A1:A1", ws.ConditionalFormats.Single().Ranges.Single().RangeAddress.ToString());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MergedCellsLooseDataValidation()
|
|
{
|
|
using (XLWorkbook wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("Sheet1");
|
|
ws.Cell("A1").NewDataValidation.WholeNumber.Between(1, 2);
|
|
ws.Cell("A2").NewDataValidation.Date.GreaterThan(new System.DateTime(2018, 1, 1));
|
|
|
|
ws.Range("A1:A2").Merge();
|
|
|
|
Assert.IsTrue(ws.Cell("A1").HasDataValidation);
|
|
Assert.AreEqual("1", ws.Cell("A1").DataValidation.MinValue);
|
|
Assert.AreEqual("2", ws.Cell("A1").DataValidation.MaxValue);
|
|
Assert.IsFalse(ws.Cell("A2").HasDataValidation);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void UnmergedCellsPreserveStyle()
|
|
{
|
|
using (XLWorkbook wb = new XLWorkbook())
|
|
{
|
|
var ws = wb.AddWorksheet("Sheet1");
|
|
var range = ws.Range("B2:D4");
|
|
range.Style.Fill.SetBackgroundColor(XLColor.Yellow);
|
|
range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thick)
|
|
.Border.SetOutsideBorderColor(XLColor.DarkBlue)
|
|
.Border.SetInsideBorder(XLBorderStyleValues.Thin)
|
|
.Border.SetInsideBorderColor(XLColor.Pink);
|
|
range.Cells().ForEach(c => c.Value = c.Address.ToString());
|
|
|
|
var firstCell = ws.Cell("B2");
|
|
firstCell.Style.Fill.SetBackgroundColor(XLColor.Red)
|
|
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
|
|
.Font.SetBold();
|
|
|
|
range.Merge();
|
|
range.Unmerge();
|
|
|
|
Assert.IsTrue(range.Cells().All(c => c.Style.Fill.BackgroundColor == XLColor.Red));
|
|
Assert.IsTrue(range.Cells().Where(c => c != firstCell).All(c => c.GetString().Length == 0));
|
|
Assert.AreEqual("B2", firstCell.Value);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("B2").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("B2").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("B2").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("B2").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("C2").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C2").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C2").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C2").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("D2").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("D2").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("D2").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("D2").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("B3").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("B3").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("B3").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("B3").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C3").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C3").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C3").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C3").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("D3").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("D3").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("D3").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("D3").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("B4").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("B4").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("B4").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("B4").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C4").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C4").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("C4").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("C4").Style.Border.LeftBorder);
|
|
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("D4").Style.Border.TopBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("D4").Style.Border.RightBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.Thick, ws.Cell("D4").Style.Border.BottomBorder);
|
|
Assert.AreEqual(XLBorderStyleValues.None, ws.Cell("D4").Style.Border.LeftBorder);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MergedRangesCellValuesShouldNotBeSet()
|
|
{
|
|
using (var workbook = new XLWorkbook())
|
|
{
|
|
var ws = workbook.AddWorksheet();
|
|
ws.Range("A2:A4").Merge();
|
|
ws.Cell("A2").Value = "1";
|
|
ws.Cell("A3").Value = "1";
|
|
ws.Cell("A4").Value = "1";
|
|
ws.Cell("B1").FormulaA1 = "SUM(A:A)";
|
|
Assert.AreEqual(1, ws.Cell("B1").Value);
|
|
}
|
|
|
|
using (var workbook = new XLWorkbook())
|
|
{
|
|
var ws = workbook.AddWorksheet();
|
|
ws.Range("A2:A4").Merge().SetValue(1);
|
|
ws.Cell("B1").FormulaA1 = "SUM(A:A)";
|
|
Assert.AreEqual(1, ws.Cell("B1").Value);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MergedRangesCellFormulasShouldNotBeSet()
|
|
{
|
|
using (var workbook = new XLWorkbook())
|
|
{
|
|
var ws = workbook.AddWorksheet();
|
|
ws.Range("A2:A4").Merge();
|
|
ws.Cell("A2").FormulaA1 = "=1";
|
|
ws.Cell("A3").FormulaA1 = "=1";
|
|
ws.Cell("A4").FormulaA1 = "=1";
|
|
ws.Cell("B1").FormulaA1 = "SUM(A:A)";
|
|
Assert.AreEqual(1, ws.Cell("B1").Value);
|
|
}
|
|
|
|
using (var workbook = new XLWorkbook())
|
|
{
|
|
var ws = workbook.AddWorksheet();
|
|
ws.Range("A2:A4").Merge();
|
|
ws.Cell("A2").SetFormulaA1("=1");
|
|
ws.Cell("A3").SetFormulaA1("=1");
|
|
ws.Cell("A4").SetFormulaA1("=1");
|
|
ws.Cell("B1").SetFormulaA1("SUM(A:A)");
|
|
Assert.AreEqual(1, ws.Cell("B1").Value);
|
|
}
|
|
|
|
using (var workbook = new XLWorkbook())
|
|
{
|
|
var ws = workbook.AddWorksheet();
|
|
ws.Range("A2:A4").Merge();
|
|
ws.Cell("A2").FormulaR1C1 = "=1";
|
|
ws.Cell("A3").FormulaR1C1 = "=1";
|
|
ws.Cell("A4").FormulaR1C1 = "=1";
|
|
ws.Cell("B1").FormulaR1C1 = "SUM(A:A)";
|
|
Assert.AreEqual(1, ws.Cell("B1").Value);
|
|
}
|
|
|
|
using (var workbook = new XLWorkbook())
|
|
{
|
|
var ws = workbook.AddWorksheet();
|
|
ws.Range("A2:A4").Merge();
|
|
ws.Cell("A2").SetFormulaR1C1("=1");
|
|
ws.Cell("A3").SetFormulaR1C1("=1");
|
|
ws.Cell("A4").SetFormulaR1C1("=1");
|
|
ws.Cell("B1").SetFormulaR1C1("SUM(A:A)");
|
|
Assert.AreEqual(1, ws.Cell("B1").Value);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void MergeSingleCellRangeDoesNothing()
|
|
{
|
|
using var wb = new XLWorkbook();
|
|
var ws = wb.AddWorksheet();
|
|
var range = ws.Range(1, 1, 1, 1);
|
|
|
|
range.Merge();
|
|
|
|
Assert.IsFalse(range.IsMerged());
|
|
Assert.AreEqual(0, ws.MergedRanges.Count);
|
|
}
|
|
}
|
|
}
|