diff --git a/ClosedXML/Excel/Cells/XLCells.cs b/ClosedXML/Excel/Cells/XLCells.cs index 48e52b4..37a8b43 100644 --- a/ClosedXML/Excel/Cells/XLCells.cs +++ b/ClosedXML/Excel/Cells/XLCells.cs @@ -217,6 +217,15 @@ } } + protected override IEnumerable Children + { + get + { + foreach (XLCell c in this) + yield return c; + } + } + public override IXLRanges RangesUsed { get diff --git a/ClosedXML/Excel/Columns/XLColumn.cs b/ClosedXML/Excel/Columns/XLColumn.cs index c660ae5..5b4fa6f 100644 --- a/ClosedXML/Excel/Columns/XLColumn.cs +++ b/ClosedXML/Excel/Columns/XLColumn.cs @@ -288,12 +288,15 @@ && !autoFilterRows.Contains(t.AutoFilter.Range.FirstRow().RowNumber())) .Select(t => t.AutoFilter.Range.FirstRow().RowNumber())); + IXLStyle cellStyle = null; foreach (XLCell c in Column(startRow, endRow).CellsUsed()) { if (c.IsMerged()) continue; - var cellStyle = c.Style; + if (cellStyle == null || cellStyle.Value != c.StyleValue) + cellStyle = c.Style; + Double thisWidthMax = 0; - Int32 textRotation = c.StyleValue.Alignment.TextRotation; + Int32 textRotation = cellStyle.Alignment.TextRotation; if (c.HasRichText || textRotation != 0 || c.InnerText.Contains(Environment.NewLine)) { var kpList = new List>(); diff --git a/ClosedXML/Excel/Ranges/XLRanges.cs b/ClosedXML/Excel/Ranges/XLRanges.cs index e523a69..27e4aaa 100644 --- a/ClosedXML/Excel/Ranges/XLRanges.cs +++ b/ClosedXML/Excel/Ranges/XLRanges.cs @@ -205,7 +205,13 @@ public override bool Equals(object obj) { - var other = (XLRanges)obj; + return Equals(obj as XLRanges); + } + + public bool Equals(XLRanges other) + { + if (other == null) + return false; return _ranges.Count == other._ranges.Count && _ranges.Select(thisRange => Enumerable.Contains(other._ranges, thisRange)).All(foundOne => foundOne); @@ -227,4 +233,4 @@ range.Select(); } } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/Style/IXLStylized.cs b/ClosedXML/Excel/Style/IXLStylized.cs index 43973aa..2a0a6cb 100644 --- a/ClosedXML/Excel/Style/IXLStylized.cs +++ b/ClosedXML/Excel/Style/IXLStylized.cs @@ -14,5 +14,7 @@ /// Immutable style /// XLStyleValue StyleValue { get; } + + void ModifyStyle(Func modification); } } diff --git a/ClosedXML/Excel/Style/XLBorder.cs b/ClosedXML/Excel/Style/XLBorder.cs index 02db92a..b29c0a3 100644 --- a/ClosedXML/Excel/Style/XLBorder.cs +++ b/ClosedXML/Excel/Style/XLBorder.cs @@ -84,10 +84,14 @@ if (_container is XLWorksheet || _container is XLConditionalFormat) { - _container.Style.Border.SetTopBorder(value); - _container.Style.Border.SetBottomBorder(value); - _container.Style.Border.SetLeftBorder(value); - _container.Style.Border.SetRightBorder(value); + Modify(k => + { + k.TopBorder = value; + k.BottomBorder = value; + k.LeftBorder = value; + k.RightBorder = value; + return k; + }); } else { @@ -111,10 +115,14 @@ if (_container is XLWorksheet || _container is XLConditionalFormat) { - _container.Style.Border.SetTopBorderColor(value); - _container.Style.Border.SetBottomBorderColor(value); - _container.Style.Border.SetLeftBorderColor(value); - _container.Style.Border.SetRightBorderColor(value); + Modify(k => + { + k.TopBorderColor = value.Key; + k.BottomBorderColor = value.Key; + k.LeftBorderColor = value.Key; + k.RightBorderColor = value.Key; + return k; + }); } else { @@ -138,11 +146,14 @@ var wsContainer = _container as XLWorksheet; if (wsContainer != null) { - //wsContainer.CellsUsed().Style.Border.SetOutsideBorder(value); - wsContainer.Style.Border.SetTopBorder(value); - wsContainer.Style.Border.SetBottomBorder(value); - wsContainer.Style.Border.SetLeftBorder(value); - wsContainer.Style.Border.SetRightBorder(value); + Modify(k => + { + k.TopBorder = value; + k.BottomBorder = value; + k.LeftBorder = value; + k.RightBorder = value; + return k; + }); } else { @@ -193,11 +204,14 @@ var wsContainer = _container as XLWorksheet; if (wsContainer != null) { - //wsContainer.CellsUsed().Style.Border.SetOutsideBorderColor(value); - wsContainer.Style.Border.SetTopBorderColor(value); - wsContainer.Style.Border.SetBottomBorderColor(value); - wsContainer.Style.Border.SetLeftBorderColor(value); - wsContainer.Style.Border.SetRightBorderColor(value); + Modify(k => + { + k.TopBorderColor = value.Key; + k.BottomBorderColor = value.Key; + k.LeftBorderColor = value.Key; + k.RightBorderColor = value.Key; + return k; + }); } else { @@ -522,4 +536,4 @@ } #endregion Overridden } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/Style/XLStyle.cs b/ClosedXML/Excel/Style/XLStyle.cs index 4a59846..5575019 100644 --- a/ClosedXML/Excel/Style/XLStyle.cs +++ b/ClosedXML/Excel/Style/XLStyle.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; @@ -80,14 +81,7 @@ if (_container != null) { - _container.InnerStyle = new XLStyle(_container, Key); - - _container.Styles.OfType() - .Where(s => s._container != _container) - .ForEach(style => - { - style.Modify(modification); - }); + _container.ModifyStyle(modification); } } diff --git a/ClosedXML/Excel/Style/XLStylizedBase.cs b/ClosedXML/Excel/Style/XLStylizedBase.cs index c09f530..2120c8e 100644 --- a/ClosedXML/Excel/Style/XLStylizedBase.cs +++ b/ClosedXML/Excel/Style/XLStylizedBase.cs @@ -1,5 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; namespace ClosedXML.Excel { @@ -39,13 +43,7 @@ /// /// Get a collection of stylized entities which current entity's style changes should be propagated to. /// - protected virtual IEnumerable Children - { - get - { - return RangesUsed.OfType(); - } - } + protected abstract IEnumerable Children { get; } public abstract IXLRanges RangesUsed { get; } @@ -76,6 +74,38 @@ Children.ForEach(child => child.SetStyle(StyleValue, true)); } } + + private static ReferenceEqualityComparer _comparer = new ReferenceEqualityComparer(); + + public void ModifyStyle(Func modification) + { + var children = GetChildrenRecursively(this); + var allChildren = children.GroupBy(child => child.StyleValue, _comparer); + + foreach (var group in allChildren) + { + var styleKey = modification(group.Key.Key); + var styleValue = XLStyleValue.FromKey(styleKey); + foreach (var child in group) + { + child.StyleValue = styleValue; + } + } + } + + private IEnumerable GetChildrenRecursively(XLStylizedBase parent) + { + return new List { parent } + .Union(parent.Children.Where(child => child != parent).SelectMany(child => GetChildrenRecursively(child))); + } #endregion Private methods + + #region Nested classes + public sealed class ReferenceEqualityComparer : IEqualityComparer where T: class + { + public bool Equals(T x, T y) => ReferenceEquals(x, y); + public int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj); + } + #endregion } } diff --git a/ClosedXML/Excel/Style/XLStylizedContainer.cs b/ClosedXML/Excel/Style/XLStylizedContainer.cs index 1f09eb9..d00901f 100644 --- a/ClosedXML/Excel/Style/XLStylizedContainer.cs +++ b/ClosedXML/Excel/Style/XLStylizedContainer.cs @@ -23,5 +23,16 @@ { get { return _container.RangesUsed; } } + + protected override IEnumerable Children + { + get + { + if (_container is XLStylizedBase) + yield return _container as XLStylizedBase; + + yield break; + } + } } } diff --git a/ClosedXML/Excel/Style/XLStylizedEmpty.cs b/ClosedXML/Excel/Style/XLStylizedEmpty.cs index c916a6a..f1173b1 100644 --- a/ClosedXML/Excel/Style/XLStylizedEmpty.cs +++ b/ClosedXML/Excel/Style/XLStylizedEmpty.cs @@ -20,5 +20,10 @@ { get { return new XLRanges(); } } + + protected override IEnumerable Children + { + get { yield break; } + } } } diff --git a/ClosedXML_Sandbox/PerformanceRunner.cs b/ClosedXML_Sandbox/PerformanceRunner.cs index d64e02b..2fdeb53 100644 --- a/ClosedXML_Sandbox/PerformanceRunner.cs +++ b/ClosedXML_Sandbox/PerformanceRunner.cs @@ -40,6 +40,35 @@ EmulateSave(workbook); } + public static void RunInsertTableWithStyles () + { + var rows = new List(); + + for (int i = 0; i < rowCount; i++) + { + var row = GenerateRow(); + rows.Add(row); + } + + var workbook = new XLWorkbook(); + var worksheet = workbook.Worksheets.Add("Sheet 1"); + worksheet.Cell(1, 1).InsertTable(rows); + + CreateMergedCell(worksheet); + + worksheet.Columns().AdjustToContents(); + + worksheet.Range(worksheet.FirstCellUsed().Address, worksheet.LastCellUsed().Address) + .Style + .Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center) + .Border.SetOutsideBorder(XLBorderStyleValues.Thick) + .Fill.SetBackgroundColor(XLColor.Red) + .Font.SetFontColor(XLColor.Blue) + .NumberFormat.SetFormat("0.00") + .Protection.SetLocked(false); + + EmulateSave(workbook); + } public static void OpenTestFile() { using (var wb = new XLWorkbook("test.xlsx")) diff --git a/ClosedXML_Sandbox/Program.cs b/ClosedXML_Sandbox/Program.cs index 3773e7e..bbbe81d 100644 --- a/ClosedXML_Sandbox/Program.cs +++ b/ClosedXML_Sandbox/Program.cs @@ -14,6 +14,10 @@ PerformanceRunner.TimeAction(PerformanceRunner.RunInsertTable); Console.WriteLine(); + Console.WriteLine("Running {0}", nameof(PerformanceRunner.RunInsertTableWithStyles)); + PerformanceRunner.TimeAction(PerformanceRunner.RunInsertTableWithStyles); + Console.WriteLine(); + Console.WriteLine("Press any key to continue"); Console.ReadKey(); }