Revert "[NWD] Further EPPlus fixes & improvements."

This reverts commit d0678ed0e8a7acc31363e9d08dc7d3899a8f2177.

Reason for revert: Workflow templates for xlsx files modify styling options in some cases.

Change-Id: Ief762b59af79be9071ff89753550b37c0da6bcbd
Reviewed-on: https://gnocchi-internal-review.git.corp.google.com/c/third_party/epplus/+/207762
Reviewed-by: Hughes Hilton <hugheshilton@google.com>
diff --git a/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingDataBarGroup.cs b/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingDataBarGroup.cs
index 91abb7d..99a44a1 100644
--- a/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingDataBarGroup.cs
+++ b/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingDataBarGroup.cs
@@ -29,6 +29,8 @@
  * Eyal Seagull    Conditional Formatting Adaption    2012-04-03
  *******************************************************************************/
 
+using System.Drawing;
+
 namespace OfficeOpenXml.ConditionalFormatting.Contracts;
 
 /// <summary>
@@ -49,4 +51,9 @@
   /// Databar High Value
   /// </summary>
   ExcelConditionalFormattingIconDataBarValue HighValue { get; }
+
+  /// <summary>
+  /// The color of the databar
+  /// </summary>
+  Color Color { get; set; }
 }
diff --git a/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingIconSetGroup.cs b/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingIconSetGroup.cs
index 770d75b..700ffd6 100644
--- a/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingIconSetGroup.cs
+++ b/EPPlus/ConditionalFormatting/Contracts/IExcelConditionalFormattingIconSetGroup.cs
@@ -44,4 +44,9 @@
   /// ShowValue
   /// </summary>
   bool ShowValue { get; set; }
+
+  /// <summary>
+  /// IconSet (3, 4 ou 5 IconSet)
+  /// </summary>
+  T IconSet { get; set; }
 }
diff --git a/EPPlus/ConditionalFormatting/Contracts/IRangeConditionalFormatting.cs b/EPPlus/ConditionalFormatting/Contracts/IRangeConditionalFormatting.cs
index bd84006..b2d99bf 100644
--- a/EPPlus/ConditionalFormatting/Contracts/IRangeConditionalFormatting.cs
+++ b/EPPlus/ConditionalFormatting/Contracts/IRangeConditionalFormatting.cs
@@ -29,6 +29,7 @@
  * Eyal Seagull		    Conditional Formatting    2012-04-03
  *******************************************************************************/
 
+using System.Drawing;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 
 namespace OfficeOpenXml.ConditionalFormatting;
@@ -283,4 +284,35 @@
   /// </summary>
   /// <returns></returns>
   IExcelConditionalFormattingTwoColorScale AddTwoColorScale();
+
+  /// <summary>
+  /// Adds a <see cref="IExcelConditionalFormattingThreeIconSet&lt;eExcelconditionalFormatting3IconsSetType&gt;"/> to the range
+  /// </summary>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  IExcelConditionalFormattingThreeIconSet<eExcelconditionalFormatting3IconsSetType> AddThreeIconSet(
+      eExcelconditionalFormatting3IconsSetType iconSet);
+
+  /// <summary>
+  /// Adds a <see cref="IExcelConditionalFormattingFourIconSet&lt;eExcelconditionalFormatting4IconsSetType&gt;"/> to the range
+  /// </summary>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  IExcelConditionalFormattingFourIconSet<eExcelconditionalFormatting4IconsSetType> AddFourIconSet(
+      eExcelconditionalFormatting4IconsSetType iconSet);
+
+  /// <summary>
+  /// Adds a <see cref="IExcelConditionalFormattingFiveIconSet"/> to the range
+  /// </summary>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  IExcelConditionalFormattingFiveIconSet AddFiveIconSet(
+      eExcelconditionalFormatting5IconsSetType iconSet);
+
+  /// <summary>
+  /// Adds a <see cref="IExcelConditionalFormattingDataBarGroup"/> to the range
+  /// </summary>
+  /// <param name="color"></param>
+  /// <returns></returns>
+  IExcelConditionalFormattingDataBarGroup AddDatabar(Color color);
 }
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs
index 08605aa..8d0ecf1 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs
@@ -29,8 +29,10 @@
  * Eyal Seagull		Conditional Formatting            2012-04-03
  *******************************************************************************/
 
+using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Drawing;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 using OfficeOpenXml.Utils;
@@ -775,4 +777,66 @@
         eExcelConditionalFormattingRuleType.TwoColorScale,
         address);
   }
+
+  /// <summary>
+  /// Add ThreeIconSet Rule
+  /// </summary>
+  /// <param name="address">The address</param>
+  /// <param name="iconSet">Type of iconset</param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingThreeIconSet<eExcelconditionalFormatting3IconsSetType> AddThreeIconSet(
+      ExcelAddress address,
+      eExcelconditionalFormatting3IconsSetType iconSet) {
+    var icon =
+        (IExcelConditionalFormattingThreeIconSet<eExcelconditionalFormatting3IconsSetType>)
+          AddRule(eExcelConditionalFormattingRuleType.ThreeIconSet, address);
+    icon.IconSet = iconSet;
+    return icon;
+  }
+
+  /// <summary>
+  /// Adds a FourIconSet rule
+  /// </summary>
+  /// <param name="address"></param>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingFourIconSet<eExcelconditionalFormatting4IconsSetType> AddFourIconSet(
+      ExcelAddress address,
+      eExcelconditionalFormatting4IconsSetType iconSet) {
+    var icon =
+        (IExcelConditionalFormattingFourIconSet<eExcelconditionalFormatting4IconsSetType>)
+          AddRule(eExcelConditionalFormattingRuleType.FourIconSet, address);
+    icon.IconSet = iconSet;
+    return icon;
+  }
+
+  /// <summary>
+  /// Adds a FiveIconSet rule
+  /// </summary>
+  /// <param name="address"></param>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingFiveIconSet AddFiveIconSet(
+      ExcelAddress address,
+      eExcelconditionalFormatting5IconsSetType iconSet) {
+    var icon = (IExcelConditionalFormattingFiveIconSet)AddRule(
+        eExcelConditionalFormattingRuleType.FiveIconSet,
+        address);
+    icon.IconSet = iconSet;
+    return icon;
+  }
+
+  /// <summary>
+  /// Adds a databar rule
+  /// </summary>
+  /// <param name="address"></param>
+  /// <param name="color"></param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingDataBarGroup AddDatabar(ExcelAddress address, Color color) {
+    var dataBar = (IExcelConditionalFormattingDataBarGroup)AddRule(
+        eExcelConditionalFormattingRuleType.DataBar,
+        address);
+    dataBar.Color = color;
+    return dataBar;
+  }
 }
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs
index 84d8fb1..a8585f0 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs
@@ -30,6 +30,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using System.Xml;
 using OfficeOpenXml.Utils;
 
@@ -62,6 +63,7 @@
   internal ExcelConditionalFormattingColorScaleValue(
       eExcelConditionalFormattingValueObjectPosition position,
       eExcelConditionalFormattingValueObjectType type,
+      Color color,
       double value,
       string formula,
       eExcelConditionalFormattingRuleType ruleType,
@@ -130,6 +132,7 @@
     Position = position;
     RuleType = ruleType;
     Type = type;
+    Color = color;
     Value = value;
     Formula = formula;
   }
@@ -150,6 +153,7 @@
   internal ExcelConditionalFormattingColorScaleValue(
       eExcelConditionalFormattingValueObjectPosition position,
       eExcelConditionalFormattingValueObjectType type,
+      Color color,
       double value,
       string formula,
       eExcelConditionalFormattingRuleType ruleType,
@@ -160,6 +164,7 @@
       : this(
           position,
           type,
+          color,
           value,
           formula,
           ruleType,
@@ -183,6 +188,7 @@
   internal ExcelConditionalFormattingColorScaleValue(
       eExcelConditionalFormattingValueObjectPosition position,
       eExcelConditionalFormattingValueObjectType type,
+      Color color,
       eExcelConditionalFormattingRuleType ruleType,
       ExcelAddress address,
       int priority,
@@ -191,6 +197,7 @@
       : this(
           position,
           type,
+          color,
           0,
           null,
           ruleType,
@@ -344,6 +351,32 @@
   }
 
   /// <summary>
+  ///
+  /// </summary>
+  public Color Color {
+    get {
+      // Color Code like "FF5B34F2"
+      var colorCode = GetXmlNodeString(
+          string.Format(
+              "{0}[position()={1}]/{2}",
+              // {0}
+              ExcelConditionalFormattingConstants.Paths._color,
+              // {1}
+              GetNodeOrder(),
+              // {2}
+              ExcelConditionalFormattingConstants.Paths._rgbAttribute));
+
+      return ExcelConditionalFormattingHelper.ConvertFromColorCode(colorCode);
+    }
+    set =>
+      // Use the color code to store (Ex. "FF5B35F2")
+      CreateNodeByOrdem(
+          eExcelConditionalFormattingValueObjectNodeType.Color,
+          ExcelConditionalFormattingConstants.Paths._rgbAttribute,
+          value.ToArgb().ToString("x"));
+  }
+
+  /// <summary>
   /// Get/Set the 'cfvo' node @val attribute
   /// </summary>
   public Double Value {
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingHelper.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingHelper.cs
index ba97c3d..0ea9a45 100644
--- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingHelper.cs
+++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingHelper.cs
@@ -30,6 +30,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using System.Globalization;
 using System.Text.RegularExpressions;
 using System.Xml;
@@ -62,6 +63,20 @@
   }
 
   /// <summary>
+  /// Convert a color code to Color Object
+  /// </summary>
+  /// <param name="colorCode">Color Code (Ex. "#FFB43C53" or "FFB43C53")</param>
+  /// <returns></returns>
+  public static Color ConvertFromColorCode(string colorCode) {
+    try {
+      return Color.FromArgb(Int32.Parse(colorCode.Replace("#", ""), NumberStyles.HexNumber));
+    } catch {
+      // Assume white is the default color (instead of giving an error)
+      return Color.White;
+    }
+  }
+
+  /// <summary>
   ///
   /// </summary>
   /// <param name="node"></param>
diff --git a/EPPlus/ConditionalFormatting/RangeConditionalFormatting.cs b/EPPlus/ConditionalFormatting/RangeConditionalFormatting.cs
index 6fcc305..debf061 100644
--- a/EPPlus/ConditionalFormatting/RangeConditionalFormatting.cs
+++ b/EPPlus/ConditionalFormatting/RangeConditionalFormatting.cs
@@ -29,6 +29,7 @@
  * Eyal Seagull       Conditional Formatting    2012-04-03
  *******************************************************************************/
 
+using System.Drawing;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
 using OfficeOpenXml.Utils;
 
@@ -377,4 +378,61 @@
         eExcelConditionalFormattingRuleType.TwoColorScale,
         _address));
   }
+
+  /// <summary>
+  /// Adds a ThreeIconSet rule
+  /// </summary>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingThreeIconSet<eExcelconditionalFormatting3IconsSetType> AddThreeIconSet(
+      eExcelconditionalFormatting3IconsSetType iconSet) {
+    var rule =
+        (IExcelConditionalFormattingThreeIconSet<eExcelconditionalFormatting3IconsSetType>)(_worksheet.ConditionalFormatting.AddRule(
+            eExcelConditionalFormattingRuleType.ThreeIconSet,
+            _address));
+    rule.IconSet = iconSet;
+    return rule;
+  }
+
+  /// <summary>
+  /// Adds a FourIconSet rule
+  /// </summary>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingFourIconSet<eExcelconditionalFormatting4IconsSetType> AddFourIconSet(
+      eExcelconditionalFormatting4IconsSetType iconSet) {
+    var rule =
+        (IExcelConditionalFormattingFourIconSet<eExcelconditionalFormatting4IconsSetType>)(_worksheet.ConditionalFormatting.AddRule(
+            eExcelConditionalFormattingRuleType.FourIconSet,
+            _address));
+    rule.IconSet = iconSet;
+    return rule;
+  }
+
+  /// <summary>
+  /// Adds a FiveIconSet rule
+  /// </summary>
+  /// <param name="iconSet"></param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingFiveIconSet AddFiveIconSet(
+      eExcelconditionalFormatting5IconsSetType iconSet) {
+    var rule = (IExcelConditionalFormattingFiveIconSet)(_worksheet.ConditionalFormatting.AddRule(
+        eExcelConditionalFormattingRuleType.FiveIconSet,
+        _address));
+    rule.IconSet = iconSet;
+    return rule;
+  }
+
+  /// <summary>
+  /// Adds a Databar rule
+  /// </summary>
+  /// <param name="color">The color of the databar</param>
+  /// <returns></returns>
+  public IExcelConditionalFormattingDataBarGroup AddDatabar(Color color) {
+    var rule = (IExcelConditionalFormattingDataBarGroup)(_worksheet.ConditionalFormatting.AddRule(
+        eExcelConditionalFormattingRuleType.DataBar,
+        _address));
+    rule.Color = color;
+    return rule;
+  }
 }
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs
index 8ca4be2..2a80337 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs
@@ -29,6 +29,7 @@
  * Eyal Seagull        Added       		  2012-04-03
  *******************************************************************************/
 
+using System.Drawing;
 using System.Globalization;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting.Contracts;
@@ -152,4 +153,17 @@
   public ExcelConditionalFormattingIconDataBarValue LowValue { get; internal set; }
 
   public ExcelConditionalFormattingIconDataBarValue HighValue { get; internal set; }
+
+  private const string _colorPath = "d:dataBar/d:color/@rgb";
+
+  public Color Color {
+    get {
+      var rgb = GetXmlNodeString(_colorPath);
+      if (!string.IsNullOrEmpty(rgb)) {
+        return Color.FromArgb(int.Parse(rgb, NumberStyles.HexNumber));
+      }
+      return Color.White;
+    }
+    set => SetXmlNodeString(_colorPath, value.ToArgb().ToString("X"));
+  }
 }
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs
index 5700e10..fa0574e 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs
@@ -71,6 +71,7 @@
     LowValue = new(
         eExcelConditionalFormattingValueObjectPosition.Low,
         eExcelConditionalFormattingValueObjectType.Min,
+        ColorTranslator.FromHtml(ExcelConditionalFormattingConstants.Colors._cfvoLowValue),
         eExcelConditionalFormattingRuleType.ThreeColorScale,
         address,
         priority,
@@ -81,6 +82,7 @@
     MiddleValue = new(
         eExcelConditionalFormattingValueObjectPosition.Middle,
         eExcelConditionalFormattingValueObjectType.Percent,
+        ColorTranslator.FromHtml(ExcelConditionalFormattingConstants.Colors._cfvoMiddleValue),
         50,
         string.Empty,
         eExcelConditionalFormattingRuleType.ThreeColorScale,
@@ -93,6 +95,7 @@
     HighValue = new(
         eExcelConditionalFormattingValueObjectPosition.High,
         eExcelConditionalFormattingValueObjectType.Max,
+        ColorTranslator.FromHtml(ExcelConditionalFormattingConstants.Colors._cfvoHighValue),
         eExcelConditionalFormattingRuleType.ThreeColorScale,
         address,
         priority,
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeIconSet.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeIconSet.cs
index bcf19e1..2b8b27e 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeIconSet.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeIconSet.cs
@@ -223,6 +223,18 @@
 
   private const string _iconSetPath = "d:iconSet/@iconSet";
 
+  /// <summary>
+  /// Type of iconset
+  /// </summary>
+  public T IconSet {
+    get {
+      var v = GetXmlNodeString(_iconSetPath);
+      v = v.Substring(1); //Skip first icon.
+      return (T)Enum.Parse(typeof(T), v, true);
+    }
+    set => SetXmlNodeString(_iconSetPath, GetIconSetString(value));
+  }
+
   private string GetIconSetString(T value) {
     if (Type == eExcelConditionalFormattingRuleType.FourIconSet) {
       switch (value.ToString()) {
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs
index ad7d79f..69ee8b1 100644
--- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs
+++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs
@@ -81,6 +81,7 @@
     LowValue = new(
         eExcelConditionalFormattingValueObjectPosition.Low,
         eExcelConditionalFormattingValueObjectType.Min,
+        ColorTranslator.FromHtml(ExcelConditionalFormattingConstants.Colors._cfvoLowValue),
         eExcelConditionalFormattingRuleType.TwoColorScale,
         address,
         priority,
@@ -91,6 +92,7 @@
     HighValue = new(
         eExcelConditionalFormattingValueObjectPosition.High,
         eExcelConditionalFormattingValueObjectType.Max,
+        ColorTranslator.FromHtml(ExcelConditionalFormattingConstants.Colors._cfvoHighValue),
         eExcelConditionalFormattingRuleType.TwoColorScale,
         address,
         priority,
diff --git a/EPPlus/DataValidation/Contracts/IExcelDataValidation.cs b/EPPlus/DataValidation/Contracts/IExcelDataValidation.cs
index 7917008..44b5d3e 100644
--- a/EPPlus/DataValidation/Contracts/IExcelDataValidation.cs
+++ b/EPPlus/DataValidation/Contracts/IExcelDataValidation.cs
@@ -49,7 +49,7 @@
   /// <summary>
   /// Controls how Excel will handle invalid values.
   /// </summary>
-  ExcelDataValidationWarningStyle ErrorStyle { get; }
+  ExcelDataValidationWarningStyle ErrorStyle { get; set; }
 
   /// <summary>
   /// True if input message should be shown
diff --git a/EPPlus/DataValidation/Contracts/IExcelDataValidationWithOperator.cs b/EPPlus/DataValidation/Contracts/IExcelDataValidationWithOperator.cs
index 2604079..48b06cb 100644
--- a/EPPlus/DataValidation/Contracts/IExcelDataValidationWithOperator.cs
+++ b/EPPlus/DataValidation/Contracts/IExcelDataValidationWithOperator.cs
@@ -39,5 +39,5 @@
   /// <summary>
   /// Operator type
   /// </summary>
-  ExcelDataValidationOperator Operator { get; }
+  ExcelDataValidationOperator Operator { get; set; }
 }
diff --git a/EPPlus/DataValidation/ExcelDataValidation.cs b/EPPlus/DataValidation/ExcelDataValidation.cs
index 88a60de..78b6259 100644
--- a/EPPlus/DataValidation/ExcelDataValidation.cs
+++ b/EPPlus/DataValidation/ExcelDataValidation.cs
@@ -199,9 +199,18 @@
   public ExcelDataValidationOperator Operator {
     get {
       var operatorString = GetXmlNodeString(_operatorPath);
-      return Enum.TryParse<ExcelDataValidationOperator>(operatorString, true, out var op)
-          ? op
-          : ExcelDataValidationOperator.Any;
+      if (!string.IsNullOrEmpty(operatorString)) {
+        return (ExcelDataValidationOperator)
+          Enum.Parse(typeof(ExcelDataValidationOperator), operatorString);
+      }
+      return default(ExcelDataValidationOperator);
+    }
+    set {
+      if (!ValidationType.AllowOperator) {
+        throw new InvalidOperationException(
+            "The current validation type does not allow operator to be set");
+      }
+      SetXmlNodeString(_operatorPath, value.ToString());
     }
   }
 
@@ -211,9 +220,17 @@
   public ExcelDataValidationWarningStyle ErrorStyle {
     get {
       var errorStyleString = GetXmlNodeString(_errorStylePath);
-      return Enum.TryParse<ExcelDataValidationWarningStyle>(errorStyleString, true, out var style)
-          ? style
-          : ExcelDataValidationWarningStyle.Undefined;
+      if (!string.IsNullOrEmpty(errorStyleString)) {
+        return (ExcelDataValidationWarningStyle)
+          Enum.Parse(typeof(ExcelDataValidationWarningStyle), errorStyleString);
+      }
+      return ExcelDataValidationWarningStyle.Undefined;
+    }
+    set {
+      if (value == ExcelDataValidationWarningStyle.Undefined) {
+        DeleteNode(_errorStylePath);
+      }
+      SetXmlNodeString(_errorStylePath, value.ToString());
     }
   }
 
diff --git a/EPPlus/DataValidation/ExcelDataValidationType.cs b/EPPlus/DataValidation/ExcelDataValidationType.cs
index cdcc99c..1c7465c 100644
--- a/EPPlus/DataValidation/ExcelDataValidationType.cs
+++ b/EPPlus/DataValidation/ExcelDataValidationType.cs
@@ -116,6 +116,34 @@
   /// </summary>
   internal bool AllowOperator { get; private set; }
 
+  /// <summary>
+  /// Returns a validation type by <see cref="eDataValidationType"/>
+  /// </summary>
+  /// <param name="type"></param>
+  /// <returns></returns>
+  internal static ExcelDataValidationType GetByValidationType(eDataValidationType type) {
+    switch (type) {
+      case eDataValidationType.Any:
+        return Any;
+      case eDataValidationType.Whole:
+        return Whole;
+      case eDataValidationType.List:
+        return List;
+      case eDataValidationType.Decimal:
+        return Decimal;
+      case eDataValidationType.TextLength:
+        return TextLength;
+      case eDataValidationType.DateTime:
+        return DateTime;
+      case eDataValidationType.Time:
+        return Time;
+      case eDataValidationType.Custom:
+        return Custom;
+      default:
+        throw new InvalidOperationException("Non supported Validationtype : " + type);
+    }
+  }
+
   internal static ExcelDataValidationType GetBySchemaName(string schemaName) {
     switch (schemaName) {
       case DataValidationSchemaNames.Any:
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs
index bd137cd..3392fc7 100644
--- a/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs
+++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs
@@ -30,6 +30,9 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
+using System;
+using System.Drawing;
+using System.Globalization;
 using System.Xml;
 
 namespace OfficeOpenXml.Drawing.Vml;
@@ -149,6 +152,179 @@
     }
   }
 
+  private const string _backgroundcolorPath = "@fillcolor";
+
+  /// <summary>
+  /// Background color
+  /// </summary>
+  public Color BackgroundColor {
+    get {
+      string col = GetXmlNodeString(_backgroundcolorPath);
+      if (col == "") {
+        return Color.FromArgb(0xff, 0xff, 0xe1);
+      }
+      if (col.StartsWith("#")) {
+        col = col.Substring(1, col.Length - 1);
+      }
+      if (int.TryParse(
+          col,
+          NumberStyles.AllowHexSpecifier,
+          CultureInfo.InvariantCulture,
+          out var res)) {
+        return Color.FromArgb(res);
+      }
+      return Color.Empty;
+    }
+    set {
+      string color = "#" + value.ToArgb().ToString("X").Substring(2, 6);
+      SetXmlNodeString(_backgroundcolorPath, color);
+      //SetXmlNode(BACKGROUNDCOLOR2_PATH, color);
+    }
+  }
+
+  private const string _linestylePath = "v:stroke/@dashstyle";
+  private const string _endcapPath = "v:stroke/@endcap";
+
+  /// <summary>
+  /// Linestyle for border
+  /// </summary>
+  public eLineStyleVml LineStyle {
+    get {
+      string v = GetXmlNodeString(_linestylePath);
+      if (v == "") {
+        return eLineStyleVml.Solid;
+      }
+      if (v == "1 1") {
+        v = GetXmlNodeString(_endcapPath);
+        return (eLineStyleVml)Enum.Parse(typeof(eLineStyleVml), v, true);
+      }
+      return (eLineStyleVml)Enum.Parse(typeof(eLineStyleVml), v, true);
+    }
+    set {
+      if (value == eLineStyleVml.Round || value == eLineStyleVml.Square) {
+        SetXmlNodeString(_linestylePath, "1 1");
+        if (value == eLineStyleVml.Round) {
+          SetXmlNodeString(_endcapPath, "round");
+        } else {
+          DeleteNode(_endcapPath);
+        }
+      } else {
+        string v = value.ToString();
+        v = v.Substring(0, 1).ToLower(CultureInfo.InvariantCulture) + v.Substring(1, v.Length - 1);
+        SetXmlNodeString(_linestylePath, v);
+        DeleteNode(_endcapPath);
+      }
+    }
+  }
+
+  private const string _linecolorPath = "@strokecolor";
+
+  /// <summary>
+  /// Line color
+  /// </summary>
+  public Color LineColor {
+    get {
+      string col = GetXmlNodeString(_linecolorPath);
+      if (col == "") {
+        return Color.Black;
+      }
+      if (col.StartsWith("#")) {
+        col = col.Substring(1, col.Length - 1);
+      }
+      if (int.TryParse(
+          col,
+          NumberStyles.AllowHexSpecifier,
+          CultureInfo.InvariantCulture,
+          out var res)) {
+        return Color.FromArgb(res);
+      }
+      return Color.Empty;
+    }
+    set {
+      string color = "#" + value.ToArgb().ToString("X").Substring(2, 6);
+      SetXmlNodeString(_linecolorPath, color);
+    }
+  }
+
+  private const string _linewidthPath = "@strokeweight";
+
+  /// <summary>
+  /// Width of the border
+  /// </summary>
+  public Single LineWidth {
+    get {
+      string wt = GetXmlNodeString(_linewidthPath);
+      if (wt == "") {
+        return (Single).75;
+      }
+      if (wt.EndsWith("pt")) {
+        wt = wt.Substring(0, wt.Length - 2);
+      }
+
+      if (Single.TryParse(wt, NumberStyles.Any, CultureInfo.InvariantCulture, out var ret)) {
+        return ret;
+      }
+      return 0;
+    }
+    set => SetXmlNodeString(_linewidthPath, value.ToString(CultureInfo.InvariantCulture) + "pt");
+  }
+
+  ///// <summary>
+  ///// Width of the Comment
+  ///// </summary>
+  //public Single Width
+  //{
+  //    get
+  //    {
+  //        string v;
+  //        GetStyle("width", out v);
+  //        if(v.EndsWith("pt"))
+  //        {
+  //            v = v.Substring(0, v.Length - 2);
+  //        }
+  //        short ret;
+  //        if (short.TryParse(v,System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out ret))
+  //        {
+  //            return ret;
+  //        }
+  //        else
+  //        {
+  //            return 0;
+  //        }
+  //    }
+  //    set
+  //    {
+  //        SetStyle("width", value.ToString("N2",CultureInfo.InvariantCulture) + "pt");
+  //    }
+  //}
+  ///// <summary>
+  ///// Height of the Comment
+  ///// </summary>
+  //public Single Height
+  //{
+  //    get
+  //    {
+  //        string v;
+  //        GetStyle("height", out v);
+  //        if (v.EndsWith("pt"))
+  //        {
+  //            v = v.Substring(0, v.Length - 2);
+  //        }
+  //        short ret;
+  //        if (short.TryParse(v, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out ret))
+  //        {
+  //            return ret;
+  //        }
+  //        else
+  //        {
+  //            return 0;
+  //        }
+  //    }
+  //    set
+  //    {
+  //        SetStyle("height", value.ToString("N2", CultureInfo.InvariantCulture) + "pt");
+  //    }
+  //}
   private const string _textboxStylePath = "v:textbox/@style";
 
   /// <summary>
diff --git a/EPPlus/EPPlusSDK.csproj b/EPPlus/EPPlusSDK.csproj
index 1941081..a26851d 100644
--- a/EPPlus/EPPlusSDK.csproj
+++ b/EPPlus/EPPlusSDK.csproj
@@ -4,6 +4,9 @@
     <RootNamespace>OfficeOpenXml</RootNamespace>
     <AssemblyName>EPPlus</AssemblyName>
     <PackageId>Appsheet.EPPlus</PackageId>
-    <Version>1.0.7</Version>
+    <Version>1.0.6</Version>
   </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="System.Drawing.Common" Version="6.0.0"/>
+  </ItemGroup>
 </Project>
diff --git a/EPPlus/ExcelComment.cs b/EPPlus/ExcelComment.cs
index 3930bde..9e7409a 100644
--- a/EPPlus/ExcelComment.cs
+++ b/EPPlus/ExcelComment.cs
@@ -124,6 +124,7 @@
       }
       return _text;
     }
+    set => RichText.Text = value;
   }
 
   /// <summary>
diff --git a/EPPlus/ExcelCommentCollection.cs b/EPPlus/ExcelCommentCollection.cs
index 9146df7..b4a60af 100644
--- a/EPPlus/ExcelCommentCollection.cs
+++ b/EPPlus/ExcelCommentCollection.cs
@@ -138,9 +138,79 @@
     }
   }
 
+  /// <summary>
+  /// Adds a comment to the top left cell of the range
+  /// </summary>
+  /// <param name="cell">The cell</param>
+  /// <param name="text">The comment text</param>
+  /// <param name="author">Author</param>
+  /// <returns>The comment</returns>
+  public ExcelComment Add(ExcelRangeBase cell, string text, string author) {
+    var elem = CommentXml.CreateElement("comment", ExcelPackage._schemaMain);
+    int ix = _comments.IndexOf(
+        ExcelCellBase.GetCellId(Worksheet.SheetID, cell._fromRow, cell._fromCol));
+    //Make sure the nodes come on order.
+    if (ix < 0 && (~ix < _comments.Count)) {
+      ix = ~ix;
+      var preComment = _comments[ix] as ExcelComment;
+      preComment._commentHelper.TopNode.ParentNode.InsertBefore(
+          elem,
+          preComment._commentHelper.TopNode);
+    } else {
+      CommentXml.SelectSingleNode("d:comments/d:commentList", NameSpaceManager).AppendChild(elem);
+    }
+    elem.SetAttribute("ref", cell.Start.Address);
+    ExcelComment comment = new ExcelComment(NameSpaceManager, elem, cell);
+    comment.RichText.Add(text);
+    if (author != "") {
+      comment.Author = author;
+    }
+    _comments.Add(comment);
+    //Check if a value exists otherwise add one so it is saved when the cells collection is iterated
+    if (!Worksheet._values.Exists(cell._fromRow, cell._fromCol)) {
+      Worksheet._values.SetValue(cell._fromRow, cell._fromCol, null);
+    }
+    return comment;
+  }
+
+  /// <summary>
+  /// Removes the comment
+  /// </summary>
+  /// <param name="comment">The comment to remove</param>
+  public void Remove(ExcelComment comment) {
+    ulong id = ExcelCellBase.GetCellId(
+        Worksheet.SheetID,
+        comment.Range._fromRow,
+        comment.Range._fromCol);
+    int ix = _comments.IndexOf(id);
+    if (ix >= 0 && comment == _comments[ix]) {
+      comment.TopNode.ParentNode.RemoveChild(comment.TopNode); //Remove VML
+      comment._commentHelper.TopNode.ParentNode.RemoveChild(comment._commentHelper.TopNode); //Remove Comment
+
+      Worksheet.VmlDrawingsComments._drawings.Delete(id);
+      _comments.Delete(id);
+    } else {
+      throw (new ArgumentException("Comment does not exist in the worksheet"));
+    }
+  }
+
   void IDisposable.Dispose() {}
 
+  /// <summary>
+  /// Removes the comment at the specified position
+  /// </summary>
+  /// <param name="index">The index</param>
+  public void RemoveAt(int index) {
+    Remove(this[index]);
+  }
+
   IEnumerator IEnumerable.GetEnumerator() {
     return _comments;
   }
+
+  internal void Clear() {
+    while (Count > 0) {
+      RemoveAt(0);
+    }
+  }
 }
diff --git a/EPPlus/ExcelPrinterSettings.cs b/EPPlus/ExcelPrinterSettings.cs
new file mode 100644
index 0000000..dcdb541
--- /dev/null
+++ b/EPPlus/ExcelPrinterSettings.cs
@@ -0,0 +1,773 @@
+/*******************************************************************************
+ * You may amend and distribute as you like, but don't remove this header!
+ *
+ * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
+ * See http://www.codeplex.com/EPPlus for details.
+ *
+ * Copyright (C) 2011  Jan Källman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
+ * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
+ *
+ * All code and executables are provided "as is" with no warranty either express or implied.
+ * The author accepts no liability for any damage or loss of business that this product may cause.
+ *
+ * Code change notes:
+ *
+ * Author							Change						Date
+ * ******************************************************************************
+ * Jan Källman		                Initial Release		        2009-10-01
+ * Jan Källman		                License changed GPL-->LGPL 2011-12-27
+ *******************************************************************************/
+
+using System;
+using System.Globalization;
+using System.Xml;
+
+namespace OfficeOpenXml;
+
+/// <summary>
+/// Printer orientation
+/// </summary>
+public enum eOrientation {
+  /// <summary>
+  /// Portrait orientation
+  /// </summary>
+  Portrait,
+
+  /// <summary>
+  /// Landscape orientation
+  /// </summary>
+  Landscape,
+}
+
+/// <summary>
+/// Papersize
+/// </summary>
+public enum ePaperSize {
+  /// <summary>
+  /// Letter paper (8.5 in. by 11 in.)
+  /// </summary>
+  Letter = 1,
+
+  /// <summary>
+  /// Letter small paper (8.5 in. by 11 in.)
+  /// </summary>
+  LetterSmall = 2,
+
+  /// <summary>
+  /// // Tabloid paper (11 in. by 17 in.)
+  /// </summary>
+  Tabloid = 3,
+
+  /// <summary>
+  /// Ledger paper (17 in. by 11 in.)
+  /// </summary>
+  Ledger = 4,
+
+  /// <summary>
+  /// Legal paper (8.5 in. by 14 in.)
+  /// </summary>
+  Legal = 5,
+
+  /// <summary>
+  /// Statement paper (5.5 in. by 8.5 in.)
+  /// </summary>
+  Statement = 6,
+
+  /// <summary>
+  /// Executive paper (7.25 in. by 10.5 in.)
+  /// </summary>
+  Executive = 7,
+
+  /// <summary>
+  /// A3 paper (297 mm by 420 mm)
+  /// </summary>
+  A3 = 8,
+
+  /// <summary>
+  /// A4 paper (210 mm by 297 mm)
+  /// </summary>
+  A4 = 9,
+
+  /// <summary>
+  /// A4 small paper (210 mm by 297 mm)
+  /// </summary>
+  A4Small = 10,
+
+  /// <summary>
+  /// A5 paper (148 mm by 210 mm)
+  /// </summary>
+  A5 = 11,
+
+  /// <summary>
+  /// B4 paper (250 mm by 353 mm)
+  /// </summary>
+  B4 = 12,
+
+  /// <summary>
+  /// B5 paper (176 mm by 250 mm)
+  /// </summary>
+  B5 = 13,
+
+  /// <summary>
+  /// Folio paper (8.5 in. by 13 in.)
+  /// </summary>
+  Folio = 14,
+
+  /// <summary>
+  /// Quarto paper (215 mm by 275 mm)
+  /// </summary>
+  Quarto = 15,
+
+  /// <summary>
+  /// Standard paper (10 in. by 14 in.)
+  /// </summary>
+  Standard1014 = 16,
+
+  /// <summary>
+  /// Standard paper (11 in. by 17 in.)
+  /// </summary>
+  Standard1117 = 17,
+
+  /// <summary>
+  /// Note paper (8.5 in. by 11 in.)
+  /// </summary>
+  Note = 18,
+
+  /// <summary>
+  /// #9 envelope (3.875 in. by 8.875 in.)
+  /// </summary>
+  Envelope9 = 19,
+
+  /// <summary>
+  /// #10 envelope (4.125 in. by 9.5 in.)
+  /// </summary>
+  Envelope10 = 20,
+
+  /// <summary>
+  /// #11 envelope (4.5 in. by 10.375 in.)
+  /// </summary>
+  Envelope11 = 21,
+
+  /// <summary>
+  /// #12 envelope (4.75 in. by 11 in.)
+  /// </summary>
+  Envelope12 = 22,
+
+  /// <summary>
+  /// #14 envelope (5 in. by 11.5 in.)
+  /// </summary>
+  Envelope14 = 23,
+
+  /// <summary>
+  /// C paper (17 in. by 22 in.)
+  /// </summary>
+  C = 24,
+
+  /// <summary>
+  /// D paper (22 in. by 34 in.)
+  /// </summary>
+  D = 25,
+
+  /// <summary>
+  /// E paper (34 in. by 44 in.)
+  /// </summary>
+  E = 26,
+
+  /// <summary>
+  /// DL envelope (110 mm by 220 mm)
+  /// </summary>
+  DlEnvelope = 27,
+
+  /// <summary>
+  /// C5 envelope (162 mm by 229 mm)
+  /// </summary>
+  C5Envelope = 28,
+
+  /// <summary>
+  /// C3 envelope (324 mm by 458 mm)
+  /// </summary>
+  C3Envelope = 29,
+
+  /// <summary>
+  /// C4 envelope (229 mm by 324 mm)
+  /// </summary>
+  C4Envelope = 30,
+
+  /// <summary>
+  /// C6 envelope (114 mm by 162 mm)
+  /// </summary>
+  C6Envelope = 31,
+
+  /// <summary>
+  /// C65 envelope (114 mm by 229 mm)
+  /// </summary>
+  C65Envelope = 32,
+
+  /// <summary>
+  /// B4 envelope (250 mm by 353 mm)
+  /// </summary>
+  B4Envelope = 33,
+
+  /// <summary>
+  /// B5 envelope (176 mm by 250 mm)
+  /// </summary>
+  B5Envelope = 34,
+
+  /// <summary>
+  /// B6 envelope (176 mm by 125 mm)
+  /// </summary>
+  B6Envelope = 35,
+
+  /// <summary>
+  /// Italy envelope (110 mm by 230 mm)
+  /// </summary>
+  ItalyEnvelope = 36,
+
+  /// <summary>
+  /// Monarch envelope (3.875 in. by 7.5 in.).
+  /// </summary>
+  MonarchEnvelope = 37,
+
+  /// <summary>
+  /// 6 3/4 envelope (3.625 in. by 6.5 in.)
+  /// </summary>
+  Six34Envelope = 38,
+
+  /// <summary>
+  /// US standard fanfold (14.875 in. by 11 in.)
+  /// </summary>
+  UsStandard = 39,
+
+  /// <summary>
+  /// German standard fanfold (8.5 in. by 12 in.)
+  /// </summary>
+  GermanStandard = 40,
+
+  /// <summary>
+  /// German legal fanfold (8.5 in. by 13 in.)
+  /// </summary>
+  GermanLegal = 41,
+
+  /// <summary>
+  /// ISO B4 (250 mm by 353 mm)
+  /// </summary>
+  Isob4 = 42,
+
+  /// <summary>
+  ///  Japanese double postcard (200 mm by 148 mm)
+  /// </summary>
+  JapaneseDoublePostcard = 43,
+
+  /// <summary>
+  /// Standard paper (9 in. by 11 in.)
+  /// </summary>
+  Standard9 = 44,
+
+  /// <summary>
+  /// Standard paper (10 in. by 11 in.)
+  /// </summary>
+  Standard10 = 45,
+
+  /// <summary>
+  /// Standard paper (15 in. by 11 in.)
+  /// </summary>
+  Standard15 = 46,
+
+  /// <summary>
+  /// Invite envelope (220 mm by 220 mm)
+  /// </summary>
+  InviteEnvelope = 47,
+
+  /// <summary>
+  /// Letter extra paper (9.275 in. by 12 in.)
+  /// </summary>
+  LetterExtra = 50,
+
+  /// <summary>
+  /// Legal extra paper (9.275 in. by 15 in.)
+  /// </summary>
+  LegalExtra = 51,
+
+  /// <summary>
+  /// Tabloid extra paper (11.69 in. by 18 in.)
+  /// </summary>
+  TabloidExtra = 52,
+
+  /// <summary>
+  /// A4 extra paper (236 mm by 322 mm)
+  /// </summary>
+  A4Extra = 53,
+
+  /// <summary>
+  /// Letter transverse paper (8.275 in. by 11 in.)
+  /// </summary>
+  LetterTransverse = 54,
+
+  /// <summary>
+  /// A4 transverse paper (210 mm by 297 mm)
+  /// </summary>
+  A4Transverse = 55,
+
+  /// <summary>
+  /// Letter extra transverse paper (9.275 in. by 12 in.)
+  /// </summary>
+  LetterExtraTransverse = 56,
+
+  /// <summary>
+  /// SuperA/SuperA/A4 paper (227 mm by 356 mm)
+  /// </summary>
+  SuperA = 57,
+
+  /// <summary>
+  /// SuperB/SuperB/A3 paper (305 mm by 487 mm)
+  /// </summary>
+  SuperB = 58,
+
+  /// <summary>
+  /// Letter plus paper (8.5 in. by 12.69 in.)
+  /// </summary>
+  LetterPlus = 59,
+
+  /// <summary>
+  /// A4 plus paper (210 mm by 330 mm)
+  /// </summary>
+  A4Plus = 60,
+
+  /// <summary>
+  /// A5 transverse paper (148 mm by 210 mm)
+  /// </summary>
+  A5Transverse = 61,
+
+  /// <summary>
+  /// JIS B5 transverse paper (182 mm by 257 mm)
+  /// </summary>
+  Jisb5Transverse = 62,
+
+  /// <summary>
+  /// A3 extra paper (322 mm by 445 mm)
+  /// </summary>
+  A3Extra = 63,
+
+  /// <summary>
+  /// A5 extra paper (174 mm by 235 mm)
+  /// </summary>
+  A5Extra = 64,
+
+  /// <summary>
+  /// ISO B5 extra paper (201 mm by 276 mm)
+  /// </summary>
+  Isob5 = 65,
+
+  /// <summary>
+  /// A2 paper (420 mm by 594 mm)
+  /// </summary>
+  A2 = 66,
+
+  /// <summary>
+  /// A3 transverse paper (297 mm by 420 mm)
+  /// </summary>
+  A3Transverse = 67,
+
+  /// <summary>
+  /// A3 extra transverse paper (322 mm by 445 mm*/
+  /// </summary>
+  A3ExtraTransverse = 68,
+}
+
+/// <summary>
+/// Specifies printed page order
+/// </summary>
+public enum ePageOrder {
+  /// <summary>
+  /// Order pages vertically first, then move horizontally.
+  /// </summary>
+  DownThenOver,
+
+  /// <summary>
+  /// Order pages horizontally first, then move vertically
+  /// </summary>
+  OverThenDown,
+}
+
+/// <summary>
+/// Printer settings
+/// </summary>
+public sealed class ExcelPrinterSettings : XmlHelper {
+  private readonly ExcelWorksheet _ws;
+  private bool _marginsCreated;
+
+  internal ExcelPrinterSettings(XmlNamespaceManager ns, XmlNode topNode, ExcelWorksheet ws)
+      : base(ns, topNode) {
+    _ws = ws;
+    SchemaNodeOrder = ws.SchemaNodeOrder;
+  }
+
+  private const string _leftMarginPath = "d:pageMargins/@left";
+
+  /// <summary>
+  /// Left margin in inches
+  /// </summary>
+  public decimal LeftMargin {
+    get => GetXmlNodeDecimal(_leftMarginPath);
+    set {
+      CreateMargins();
+      SetXmlNodeString(_leftMarginPath, value.ToString(CultureInfo.InvariantCulture));
+    }
+  }
+
+  private const string _rightMarginPath = "d:pageMargins/@right";
+
+  /// <summary>
+  /// Right margin in inches
+  /// </summary>
+  public decimal RightMargin {
+    get => GetXmlNodeDecimal(_rightMarginPath);
+    set {
+      CreateMargins();
+      SetXmlNodeString(_rightMarginPath, value.ToString(CultureInfo.InvariantCulture));
+    }
+  }
+
+  private const string _topMarginPath = "d:pageMargins/@top";
+
+  /// <summary>
+  /// Top margin in inches
+  /// </summary>
+  public decimal TopMargin {
+    get => GetXmlNodeDecimal(_topMarginPath);
+    set {
+      CreateMargins();
+      SetXmlNodeString(_topMarginPath, value.ToString(CultureInfo.InvariantCulture));
+    }
+  }
+
+  private const string _bottomMarginPath = "d:pageMargins/@bottom";
+
+  /// <summary>
+  /// Bottom margin in inches
+  /// </summary>
+  public decimal BottomMargin {
+    get => GetXmlNodeDecimal(_bottomMarginPath);
+    set {
+      CreateMargins();
+      SetXmlNodeString(_bottomMarginPath, value.ToString(CultureInfo.InvariantCulture));
+    }
+  }
+
+  private const string _headerMarginPath = "d:pageMargins/@header";
+
+  /// <summary>
+  /// Header margin in inches
+  /// </summary>
+  public decimal HeaderMargin {
+    get => GetXmlNodeDecimal(_headerMarginPath);
+    set {
+      CreateMargins();
+      SetXmlNodeString(_headerMarginPath, value.ToString(CultureInfo.InvariantCulture));
+    }
+  }
+
+  private const string _footerMarginPath = "d:pageMargins/@footer";
+
+  /// <summary>
+  /// Footer margin in inches
+  /// </summary>
+  public decimal FooterMargin {
+    get => GetXmlNodeDecimal(_footerMarginPath);
+    set {
+      CreateMargins();
+      SetXmlNodeString(_footerMarginPath, value.ToString(CultureInfo.InvariantCulture));
+    }
+  }
+
+  private const string _orientationPath = "d:pageSetup/@orientation";
+
+  /// <summary>
+  /// Orientation
+  /// Portrait or Landscape
+  /// </summary>
+  public eOrientation Orientation {
+    get => (eOrientation)Enum.Parse(typeof(eOrientation), GetXmlNodeString(_orientationPath), true);
+    set =>
+      SetXmlNodeString(_orientationPath, value.ToString().ToLower(CultureInfo.InvariantCulture));
+  }
+
+  private const string _fitToWidthPath = "d:pageSetup/@fitToWidth";
+
+  /// <summary>
+  /// Fit to Width in pages.
+  /// Set FitToPage to true when using this one.
+  /// 0 is automatic
+  /// </summary>
+  public int FitToWidth {
+    get => GetXmlNodeInt(_fitToWidthPath);
+    set => SetXmlNodeString(_fitToWidthPath, value.ToString());
+  }
+
+  private const string _fitToHeightPath = "d:pageSetup/@fitToHeight";
+
+  /// <summary>
+  /// Fit to height in pages.
+  /// Set FitToPage to true when using this one.
+  /// 0 is automatic
+  /// </summary>
+  public int FitToHeight {
+    get => GetXmlNodeInt(_fitToHeightPath);
+    set => SetXmlNodeString(_fitToHeightPath, value.ToString());
+  }
+
+  private const string _scalePath = "d:pageSetup/@scale";
+
+  /// <summary>
+  /// Print scale
+  /// </summary>
+  public int Scale {
+    get => GetXmlNodeInt(_scalePath);
+    set => SetXmlNodeString(_scalePath, value.ToString());
+  }
+
+  private const string _fitToPagePath = "d:sheetPr/d:pageSetUpPr/@fitToPage";
+
+  /// <summary>
+  /// Fit To Page.
+  /// </summary>
+  public bool FitToPage {
+    get => GetXmlNodeBool(_fitToPagePath);
+    set => SetXmlNodeString(_fitToPagePath, value ? "1" : "0");
+  }
+
+  private const string _headersPath = "d:printOptions/@headings";
+
+  /// <summary>
+  /// Print headings (column letter and row numbers)
+  /// </summary>
+  public bool ShowHeaders {
+    get => GetXmlNodeBool(_headersPath, false);
+    set => SetXmlNodeBool(_headersPath, value, false);
+  }
+
+  /// <summary>
+  /// Print titles
+  /// Rows to be repeated after each pagebreak.
+  /// The address must be a full row address (ex. 1:1)
+  /// </summary>
+  public ExcelAddress RepeatRows {
+    get {
+      if (_ws.Names.ContainsKey("_xlnm.Print_Titles")) {
+        ExcelRangeBase r = _ws.Names["_xlnm.Print_Titles"];
+        if (r.Start.Column == 1 && r.End.Column == ExcelPackage.MaxColumns) {
+          return new(r.FirstAddress);
+        }
+        if (r._addresses != null
+            && r.Addresses[0].Start.Column == 1
+            && r.Addresses[0].End.Column == ExcelPackage.MaxColumns) {
+          return r._addresses[0];
+        }
+        return null;
+      }
+      return null;
+    }
+    set {
+      //Must span entire columns
+      if (!(value.Start.Column == 1 && value.End.Column == ExcelPackage.MaxColumns)) {
+        throw new InvalidOperationException(
+            "Address must span full columns only (for ex. Address=\"A:A\" for the first column).");
+      }
+
+      var vertAddr = RepeatColumns;
+      string addr;
+      if (vertAddr == null) {
+        addr = value.Address;
+      } else {
+        addr = vertAddr.Address + "," + value.Address;
+      }
+
+      if (_ws.Names.ContainsKey("_xlnm.Print_Titles")) {
+        _ws.Names["_xlnm.Print_Titles"].Address = addr;
+      } else {
+        _ws.Names.Add("_xlnm.Print_Titles", new(_ws, addr));
+      }
+    }
+  }
+
+  /// <summary>
+  /// Print titles
+  /// Columns to be repeated after each pagebreak.
+  /// The address must be a full column address (ex. A:A)
+  /// </summary>
+  public ExcelAddress RepeatColumns {
+    get {
+      if (_ws.Names.ContainsKey("_xlnm.Print_Titles")) {
+        ExcelRangeBase r = _ws.Names["_xlnm.Print_Titles"];
+        if (r.Start.Row == 1 && r.End.Row == ExcelPackage.MaxRows) {
+          return new(r.FirstAddress);
+        }
+        if (r._addresses != null
+            && (r._addresses[0].Start.Row == 1
+                    && r._addresses[0].End.Row == ExcelPackage.MaxRows)) {
+          return r._addresses[0];
+        }
+        return null;
+      }
+      return null;
+    }
+    set {
+      //Must span entire rows
+      if (!(value.Start.Row == 1 && value.End.Row == ExcelPackage.MaxRows)) {
+        throw new InvalidOperationException(
+            "Address must span rows only (for ex. Address=\"1:1\" for the first row).");
+      }
+
+      var horAddr = RepeatRows;
+      string addr;
+      if (horAddr == null) {
+        addr = value.Address;
+      } else {
+        addr = value.Address + "," + horAddr.Address;
+      }
+
+      if (_ws.Names.ContainsKey("_xlnm.Print_Titles")) {
+        _ws.Names["_xlnm.Print_Titles"].Address = addr;
+      } else {
+        _ws.Names.Add("_xlnm.Print_Titles", new(_ws, addr));
+      }
+    }
+  }
+
+  /// <summary>
+  /// The printarea.
+  /// Null if no print area is set.
+  /// </summary>
+  public ExcelRangeBase PrintArea {
+    get {
+      if (_ws.Names.ContainsKey("_xlnm.Print_Area")) {
+        return _ws.Names["_xlnm.Print_Area"];
+      }
+      return null;
+    }
+    set {
+      if (value == null) {
+        _ws.Names.Remove("_xlnm.Print_Area");
+      } else if (_ws.Names.ContainsKey("_xlnm.Print_Area")) {
+        _ws.Names["_xlnm.Print_Area"].Address = value.Address;
+      } else {
+        _ws.Names.Add("_xlnm.Print_Area", value);
+      }
+    }
+  }
+
+  private const string _gridLinesPath = "d:printOptions/@gridLines";
+
+  /// <summary>
+  /// Print gridlines
+  /// </summary>
+  public bool ShowGridLines {
+    get => GetXmlNodeBool(_gridLinesPath, false);
+    set => SetXmlNodeBool(_gridLinesPath, value, false);
+  }
+
+  private const string _horizontalCenteredPath = "d:printOptions/@horizontalCentered";
+
+  /// <summary>
+  /// Horizontal centered when printing
+  /// </summary>w
+  public bool HorizontalCentered {
+    get => GetXmlNodeBool(_horizontalCenteredPath, false);
+    set => SetXmlNodeBool(_horizontalCenteredPath, value, false);
+  }
+
+  private const string _verticalCenteredPath = "d:printOptions/@verticalCentered";
+
+  /// <summary>
+  /// Vertical centered when printing
+  /// </summary>
+  public bool VerticalCentered {
+    get => GetXmlNodeBool(_verticalCenteredPath, false);
+    set => SetXmlNodeBool(_verticalCenteredPath, value, false);
+  }
+
+  private const string _pageOrderPath = "d:pageSetup/@pageOrder";
+
+  /// <summary>
+  /// Specifies printed page order
+  /// </summary>
+  public ePageOrder PageOrder {
+    get {
+      if (GetXmlNodeString(_pageOrderPath) == "overThenDown") {
+        return ePageOrder.OverThenDown;
+      }
+      return ePageOrder.DownThenOver;
+    }
+    set {
+      if (value == ePageOrder.OverThenDown) {
+        SetXmlNodeString(_pageOrderPath, "overThenDown");
+      } else {
+        DeleteNode(_pageOrderPath);
+      }
+    }
+  }
+
+  private const string _blackAndWhitePath = "d:pageSetup/@blackAndWhite";
+
+  /// <summary>
+  /// Print black and white
+  /// </summary>
+  public bool BlackAndWhite {
+    get => GetXmlNodeBool(_blackAndWhitePath, false);
+    set => SetXmlNodeBool(_blackAndWhitePath, value, false);
+  }
+
+  private const string _draftPath = "d:pageSetup/@draft";
+
+  /// <summary>
+  /// Print a draft
+  /// </summary>
+  public bool Draft {
+    get => GetXmlNodeBool(_draftPath, false);
+    set => SetXmlNodeBool(_draftPath, value, false);
+  }
+
+  private const string _paperSizePath = "d:pageSetup/@paperSize";
+
+  /// <summary>
+  /// Paper size
+  /// </summary>
+  public ePaperSize PaperSize {
+    get {
+      string s = GetXmlNodeString(_paperSizePath);
+      if (s != "") {
+        return (ePaperSize)int.Parse(s);
+      }
+      return ePaperSize.Letter;
+    }
+    set => SetXmlNodeString(_paperSizePath, ((int)value).ToString());
+  }
+
+  /// <summary>
+  /// All or none of the margin attributes must exist. Create all att ones.
+  /// </summary>
+  private void CreateMargins() {
+    if (_marginsCreated == false
+        && TopNode.SelectSingleNode(_leftMarginPath, NameSpaceManager) == null) {
+      _marginsCreated = true;
+      LeftMargin = 0.7087M;
+      RightMargin = 0.7087M;
+      TopMargin = 0.7480M;
+      BottomMargin = 0.7480M;
+      HeaderMargin = 0.315M;
+      FooterMargin = 0.315M;
+    }
+  }
+}
diff --git a/EPPlus/ExcelRangeBase.cs b/EPPlus/ExcelRangeBase.cs
index 686eb78..b4e48a8 100644
--- a/EPPlus/ExcelRangeBase.cs
+++ b/EPPlus/ExcelRangeBase.cs
@@ -349,6 +349,12 @@
     }
   }
 
+  private void Set_Comment(object value, int row, int col) {
+    string[] v = (string[])value;
+    Worksheet.Comments.Add(new(_worksheet, GetAddress(_fromRow, _fromCol)), v[0], v[1]);
+    //   _worksheet.Cell(row, col).Comment = comment;
+  }
+
   private void SetToSelectedRange() {
     if (_worksheet.View.SelectedRange == "") {
       Address = "A1";
@@ -1831,6 +1837,339 @@
   }
 
   /// <summary>
+  /// Adds a new comment for the range.
+  /// If this range contains more than one cell, the top left comment is returned by the method.
+  /// </summary>
+  /// <param name="text"></param>
+  /// <param name="author"></param>
+  /// <returns>A reference comment of the top left cell</returns>
+  public ExcelComment AddComment(string text, string author) {
+    if (string.IsNullOrEmpty(author)) {
+      author = Thread.CurrentPrincipal.Identity.Name;
+    }
+    //Check if any comments exists in the range and throw an exception
+    _changePropMethod(Exists_Comment, null);
+    //Create the comments
+    _changePropMethod(Set_Comment, new[] { text, author });
+
+    return _worksheet.Comments[new ExcelCellAddress(_fromRow, _fromCol)];
+  }
+
+  ///// <summary>
+  ///// Copies the range of cells to an other range
+  ///// </summary>
+  ///// <param name="Destination">The start cell where the range will be copied.</param>
+  public void Copy(ExcelRangeBase destination) {
+    bool sameWorkbook = destination._worksheet.Workbook == _worksheet.Workbook;
+    ExcelStyles sourceStyles = _worksheet.Workbook.Styles,
+        styles = destination._worksheet.Workbook.Styles;
+    Dictionary<int, int> styleCashe = new Dictionary<int, int>();
+
+    //Clear all existing cells;
+    int toRow = _toRow - _fromRow + 1,
+        toCol = _toCol - _fromCol + 1;
+
+    string s = "";
+    int i = 0;
+    object o = null;
+    byte flag = 0;
+    Uri hl = null;
+    ExcelComment comment = null;
+
+    var cse = new CellsStoreEnumerator<object>(
+        _worksheet._values,
+        _fromRow,
+        _fromCol,
+        _toRow,
+        _toCol);
+    var copiedValue = new List<CopiedCell>();
+    while (cse.Next()) {
+      var row = cse.Row;
+      var col = cse.Column; //Issue 15070
+      var cell = new CopiedCell {
+        Row = destination._fromRow + (row - _fromRow),
+        Column = destination._fromCol + (col - _fromCol),
+        Value = cse.Value,
+      };
+
+      //Destination._worksheet._values.SetValue(row, col, cse.Value);
+
+      if (_worksheet._types.Exists(row, col, ref s)) {
+        //Destination._worksheet._types.SetValue(row, col,s);
+        cell.Type = s;
+      }
+
+      if (_worksheet._formulas.Exists(row, col, ref o)) {
+        if (o is int) {
+          // Destination._worksheet._formulas.SetValue(row, col, _worksheet.GetFormula(cse.Row, cse.Column));    //Shared formulas, set the formula per cell to simplify
+          cell.Formula = _worksheet.GetFormula(cse.Row, cse.Column);
+        } else {
+          //Destination._worksheet._formulas.SetValue(row, col, o);
+          cell.Formula = o;
+        }
+      }
+      if (_worksheet._styles.Exists(row, col, ref i)) {
+        if (sameWorkbook) {
+          //Destination._worksheet._styles.SetValue(row, col, i);
+          cell.StyleID = i;
+        } else {
+          if (styleCashe.ContainsKey(i)) {
+            i = styleCashe[i];
+          } else {
+            var oldStyleId = i;
+            i = styles.CloneStyle(sourceStyles, i);
+            styleCashe.Add(oldStyleId, i);
+          }
+          //Destination._worksheet._styles.SetValue(row, col, i);
+          cell.StyleID = i;
+        }
+      }
+
+      if (_worksheet._hyperLinks.Exists(row, col, ref hl)) {
+        //Destination._worksheet._hyperLinks.SetValue(row, col, hl);
+        cell.HyperLink = hl;
+      }
+
+      if (_worksheet._commentsStore.Exists(row, col, ref comment)) {
+        cell.Comment = comment;
+      }
+
+      if (_worksheet._flags.Exists(row, col, ref flag)) {
+        cell.Flag = flag;
+      }
+      copiedValue.Add(cell);
+    }
+
+    //Copy styles with no cell value
+    var cses = new CellsStoreEnumerator<int>(
+        _worksheet._styles,
+        _fromRow,
+        _fromCol,
+        _toRow,
+        _toCol);
+    while (cses.Next()) {
+      if (!_worksheet._values.Exists(cses.Row, cses.Column)) {
+        var row = destination._fromRow + (cses.Row - _fromRow);
+        var col = destination._fromCol + (cses.Column - _fromCol);
+        var cell = new CopiedCell {
+          Row = row,
+          Column = col,
+          Value = null,
+        };
+
+        i = cses.Value;
+        if (sameWorkbook) {
+          cell.StyleID = i;
+        } else {
+          if (styleCashe.ContainsKey(i)) {
+            i = styleCashe[i];
+          } else {
+            var oldStyleId = i;
+            i = styles.CloneStyle(sourceStyles, i);
+            styleCashe.Add(oldStyleId, i);
+          }
+          //Destination._worksheet._styles.SetValue(row, col, i);
+          cell.StyleID = i;
+        }
+        copiedValue.Add(cell);
+      }
+    }
+    var copiedMergedCells = new Dictionary<int, ExcelAddress>();
+    //Merged cells
+    var csem = new CellsStoreEnumerator<int>(
+        _worksheet.MergedCells._cells,
+        _fromRow,
+        _fromCol,
+        _toRow,
+        _toCol);
+    while (csem.Next()) {
+      if (!copiedMergedCells.ContainsKey(csem.Value)) {
+        var adr = new ExcelAddress(_worksheet.Name, _worksheet.MergedCells.List[csem.Value]);
+        if (Collide(adr) == eAddressCollition.Inside) {
+          copiedMergedCells.Add(
+              csem.Value,
+              new(
+                  destination._fromRow + (adr.Start.Row - _fromRow),
+                  destination._fromCol + (adr.Start.Column - _fromCol),
+                  destination._fromRow + (adr.End.Row - _fromRow),
+                  destination._fromCol + (adr.End.Column - _fromCol)));
+        } else {
+          //Partial merge of the address ignore.
+          copiedMergedCells.Add(csem.Value, null);
+        }
+      }
+    }
+
+    destination._worksheet.MergedCells.Clear(
+        new(
+            destination._fromRow,
+            destination._fromCol,
+            destination._fromRow + toRow - 1,
+            destination._fromCol + toCol - 1));
+
+    destination._worksheet._values.Clear(destination._fromRow, destination._fromCol, toRow, toCol);
+    destination._worksheet._formulas.Clear(
+        destination._fromRow,
+        destination._fromCol,
+        toRow,
+        toCol);
+    destination._worksheet._styles.Clear(destination._fromRow, destination._fromCol, toRow, toCol);
+    destination._worksheet._types.Clear(destination._fromRow, destination._fromCol, toRow, toCol);
+    destination._worksheet._hyperLinks.Clear(
+        destination._fromRow,
+        destination._fromCol,
+        toRow,
+        toCol);
+    destination._worksheet._flags.Clear(destination._fromRow, destination._fromCol, toRow, toCol);
+    destination._worksheet._commentsStore.Clear(
+        destination._fromRow,
+        destination._fromCol,
+        toRow,
+        toCol);
+
+    foreach (var cell in copiedValue) {
+      destination._worksheet._values.SetValue(cell.Row, cell.Column, cell.Value);
+
+      if (cell.Type != null) {
+        destination._worksheet._types.SetValue(cell.Row, cell.Column, cell.Type);
+      }
+
+      if (cell.StyleID != null) {
+        destination._worksheet._styles.SetValue(cell.Row, cell.Column, cell.StyleID.Value);
+      }
+
+      if (cell.Formula != null) {
+        cell.Formula = UpdateFormulaReferences(
+            cell.Formula.ToString(),
+            destination._fromRow - _fromRow,
+            destination._fromCol - _fromCol,
+            0,
+            0,
+            true);
+        destination._worksheet._formulas.SetValue(cell.Row, cell.Column, cell.Formula);
+      }
+      if (cell.HyperLink != null) {
+        destination._worksheet._hyperLinks.SetValue(cell.Row, cell.Column, cell.HyperLink);
+      }
+
+      if (cell.Comment != null) {
+        //Destination._worksheet._commentsStore.SetValue(cell.Row, cell.Column, cell.Comment);
+      }
+      if (cell.Flag != 0) {
+        destination._worksheet._flags.SetValue(cell.Row, cell.Column, cell.Flag);
+      }
+    }
+
+    //Add merged cells
+    foreach (var m in copiedMergedCells.Values) {
+      if (m != null) {
+        destination._worksheet.MergedCells.Add(m, true);
+      }
+    }
+
+    //Clone the cell
+    //var copiedCell = (_worksheet._cells[GetCellID(_worksheet.SheetID, cell._fromRow, cell.column)] as ExcelCell);
+
+    //var newCell = copiedCell.Clone(Destination._worksheet,
+    //        Destination._fromRow + (copiedCell.Row - _fromRow),
+    //        Destination.column + (copiedCell.Column - column));
+
+    //        newCell.MergeId = _worksheet.GetMergeCellId(copiedCell.Row, copiedCell.Column);
+
+    //        if (!string.IsNullOrEmpty(newCell.Formula))
+    //        {
+    //            newCell.Formula = ExcelCell.UpdateFormulaReferences(newCell.Formula, newCell.Row - copiedCell.Row, (newCell.Column - copiedCell.Column), 1, 1);
+    //        }
+
+    //        //If its not the same workbook we must copy the styles to the new workbook.
+    //        if (!sameWorkbook)
+    //        {
+    //            if (styleCashe.ContainsKey(cell.StyleID))
+    //            {
+    //                newCell.StyleID = styleCashe[cell.StyleID];
+    //            }
+    //            else
+    //            {
+    //                newCell.StyleID = styles.CloneStyle(sourceStyles, cell.StyleID);
+    //                styleCashe.Add(cell.StyleID, newCell.StyleID);
+    //            }
+    //        }
+    //        newCells.Add(newCell);
+    //        if (newCell.Merge) mergedCells.Add(newCell.CellID, newCell);
+    //    }
+
+    //    //Now clear the destination.
+    //    Destination.Offset(0, 0, (_toRow - _fromRow) + 1, (_toCol - column) + 1).Clear();
+
+    //    //And last add the new cells to the worksheet
+    //    foreach (var cell in newCells)
+    //    {
+    //        Destination.Worksheet._cells.Add(cell);
+    //    }
+    //    //Add merged cells
+    //    if (mergedCells.Count > 0)
+    //    {
+    //        List<ExcelAddressBase> mergedAddresses = new List<ExcelAddressBase>();
+    //        foreach (var cell in mergedCells.Values)
+    //        {
+    //            if (!IsAdded(cell, mergedAddresses))
+    //            {
+    //                int startRow = cell.Row, startCol = cell.Column, endRow = cell.Row, endCol = cell.Column + 1;
+    //                while (mergedCells.ContainsKey(ExcelCell.GetCellID(Destination.Worksheet.SheetID, endRow, endCol)))
+    //                {
+    //                    ExcelCell next = mergedCells[ExcelCell.GetCellID(Destination.Worksheet.SheetID, endRow, endCol)];
+    //                    if (cell.MergeId != next.MergeId)
+    //                    {
+    //                        break;
+    //                    }
+    //                    endCol++;
+    //                }
+
+    //                while (IsMerged(mergedCells, Destination.Worksheet, endRow, startCol, endCol - 1, cell))
+    //                {
+    //                    endRow++;
+    //                }
+
+    //                mergedAddresses.Add(new ExcelAddressBase(startRow, startCol, endRow - 1, endCol - 1));
+    //            }
+    //        }
+    //        Destination.Worksheet.MergedCells.List.AddRange((from r in mergedAddresses select r.Address));
+    //    }
+    //}
+
+    //private bool IsAdded(ExcelCell cell, List<ExcelAddressBase> mergedAddresses)
+    //{
+    //    foreach (var address in mergedAddresses)
+    //    {
+    //        if (address.Collide(new ExcelAddressBase(cell.CellAddress)) == eAddressCollition.Inside)
+    //        {
+    //            return true;
+    //        }
+    //    }
+    //    return false;
+    //}
+
+    //private bool IsMerged(Dictionary<ulong, ExcelCell> mergedCells, ExcelWorksheet worksheet, int row, int startCol, int endCol, ExcelCell cell)
+    //{
+    //    for (int col = startCol; col <= endCol; col++)
+    //    {
+    //        if (!mergedCells.ContainsKey(ExcelCell.GetCellID(worksheet.SheetID, row, col)))
+    //        {
+    //            return false;
+    //        }
+    //        else
+    //        {
+    //            ExcelCell next = mergedCells[ExcelCell.GetCellID(worksheet.SheetID, row, col)];
+    //            if (cell.MergeId != next.MergeId)
+    //            {
+    //                return false;
+    //            }
+    //        }
+    //    }
+    //    return true;
+  }
+
+  /// <summary>
   /// Clear all cells
   /// </summary>
   public void Clear() {
diff --git a/EPPlus/ExcelStyles.cs b/EPPlus/ExcelStyles.cs
index 0fd1fb3..9f1b283 100644
--- a/EPPlus/ExcelStyles.cs
+++ b/EPPlus/ExcelStyles.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Xml;
 using OfficeOpenXml.ConditionalFormatting;
@@ -162,7 +163,215 @@
     if (id < 0) {
       id = 0;
     }
-    return new(this, positionId, address, id);
+    return new(this, PropertyChange, positionId, address, id);
+  }
+
+  /// <summary>
+  /// Handels changes of properties on the style objects
+  /// </summary>
+  /// <param name="sender"></param>
+  /// <param name="e"></param>
+  /// <returns></returns>
+  internal int PropertyChange(StyleBase sender, StyleChangeEventArgs e) {
+    var address = new ExcelAddressBase(e.Address);
+    var ws = _wb.Worksheets[e.PositionID];
+    Dictionary<int, int> styleCashe = new Dictionary<int, int>();
+    //Set single address
+    lock (ws._styles) {
+      SetStyleAddress(sender, e, address, ws, ref styleCashe);
+      if (address.Addresses != null) {
+        //Handle multiaddresses
+        foreach (var innerAddress in address.Addresses) {
+          SetStyleAddress(sender, e, innerAddress, ws, ref styleCashe);
+        }
+      }
+    }
+    return 0;
+  }
+
+  private void SetStyleAddress(
+      StyleBase sender,
+      StyleChangeEventArgs e,
+      ExcelAddressBase address,
+      ExcelWorksheet ws,
+      ref Dictionary<int, int> styleCashe) {
+    if (address.Start.Column == 0 || address.Start.Row == 0) {
+      throw (new("error address"));
+    }
+    //Columns
+    if (address.Start.Row == 1 && address.End.Row == ExcelPackage.MaxRows) {
+      ExcelColumn column;
+      int col = address.Start.Column,
+          row = 0;
+      //Get the startcolumn
+      if (!ws._values.Exists(0, address.Start.Column)) {
+        column = ws.Column(address.Start.Column);
+      } else {
+        column = (ExcelColumn)ws._values.GetValue(0, address.Start.Column);
+      }
+
+      while (column.ColumnMin <= address.End.Column) {
+        if (column.ColumnMax > address.End.Column) {
+          var newCol = ws.CopyColumn(column, address.End.Column + 1, column.ColumnMax);
+          column.ColumnMax = address.End.Column;
+        }
+        var s = ws._styles.GetValue(0, column.ColumnMin);
+        if (styleCashe.ContainsKey(s)) {
+          ws.SetStyle(0, column.ColumnMin, styleCashe[s]);
+        } else {
+          ExcelXfs st = CellXfs[s];
+          int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+          styleCashe.Add(s, newId);
+          ws.SetStyle(0, column.ColumnMin, newId);
+        }
+
+        //index++;
+
+        if (!ws._values.NextCell(ref row, ref col) || row > 0) {
+          column._columnMax = address.End.Column;
+          break;
+        }
+        column = (ws._values.GetValue(0, col) as ExcelColumn);
+      }
+
+      if (column._columnMax < address.End.Column) {
+        var newCol = ws.Column(column._columnMax + 1);
+        newCol._columnMax = address.End.Column;
+
+        var s = ws._styles.GetValue(0, column.ColumnMin);
+        if (styleCashe.ContainsKey(s)) {
+          ws.SetStyle(0, column.ColumnMin, styleCashe[s]);
+        } else {
+          ExcelXfs st = CellXfs[s];
+          int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+          styleCashe.Add(s, newId);
+          ws.SetStyle(0, column.ColumnMin, newId);
+        }
+
+        column._columnMax = address.End.Column;
+      }
+
+      //Set for individual cells in the span. We loop all cells here since the cells are sorted with columns first.
+      var cse = new CellsStoreEnumerator<int>(
+          ws._styles,
+          1,
+          address._fromCol,
+          address._toRow,
+          address._toCol);
+      while (cse.Next()) {
+        if (cse.Column >= address.Start.Column && cse.Column <= address.End.Column) {
+          if (styleCashe.ContainsKey(cse.Value)) {
+            ws.SetStyle(cse.Row, cse.Column, styleCashe[cse.Value]);
+          } else {
+            ExcelXfs st = CellXfs[cse.Value];
+            int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+            styleCashe.Add(cse.Value, newId);
+            cse.Value = newId;
+            //ws.SetStyle(cse.Row, cse.Column, newId);
+          }
+        }
+      }
+
+      //Update cells with styled columns
+      cse = new(ws._styles, 1, 0, address._toRow, 0);
+      while (cse.Next()) {
+        for (int c = address._fromRow; c <= address._toCol; c++) {
+          if (!ws._styles.Exists(cse.Row, c)) {
+            if (styleCashe.ContainsKey(cse.Value)) {
+              ws.SetStyle(cse.Row, c, styleCashe[cse.Value]);
+            } else {
+              ExcelXfs st = CellXfs[cse.Value];
+              int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+              styleCashe.Add(cse.Value, newId);
+              ws.SetStyle(cse.Row, c, newId);
+            }
+          }
+        }
+      }
+    }
+    //Rows
+    else if (address.Start.Column == 1 && address.End.Column == ExcelPackage.MaxColumns) {
+      for (int rowNum = address.Start.Row; rowNum <= address.End.Row; rowNum++) {
+        var s = ws._styles.GetValue(rowNum, 0);
+        if (s == 0) {
+          //iterate all columns and set the row to the style of the last column
+          var cse = new CellsStoreEnumerator<int>(ws._styles, 0, 1, 0, ExcelPackage.MaxColumns);
+          while (cse.Next()) {
+            s = cse.Value;
+            var c = ws._values.GetValue(cse.Row, cse.Column) as ExcelColumn;
+            if (c != null && c.ColumnMax < ExcelPackage.MaxColumns) {
+              for (int col = c.ColumnMin; col < c.ColumnMax; col++) {
+                if (!ws._styles.Exists(rowNum, col)) {
+                  ws._styles.SetValue(rowNum, col, s);
+                }
+              }
+            }
+          }
+          ws.SetStyle(rowNum, 0, s);
+        }
+        if (styleCashe.ContainsKey(s)) {
+          ws.SetStyle(rowNum, 0, styleCashe[s]);
+        } else {
+          ExcelXfs st = CellXfs[s];
+          int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+          styleCashe.Add(s, newId);
+          ws._styles.SetValue(rowNum, 0, newId);
+          ws.SetStyle(rowNum, 0, newId);
+        }
+      }
+
+      //Update individual cells
+      var cse2 = new CellsStoreEnumerator<int>(
+          ws._styles,
+          address._fromRow,
+          address._fromCol,
+          address._toRow,
+          address._toCol);
+      while (cse2.Next()) {
+        var s = cse2.Value;
+        if (styleCashe.ContainsKey(s)) {
+          ws.SetStyle(cse2.Row, cse2.Column, styleCashe[s]);
+        } else {
+          ExcelXfs st = CellXfs[s];
+          int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+          styleCashe.Add(s, newId);
+          cse2.Value = newId;
+        }
+      }
+
+      //Update cells with styled rows
+      cse2 = new(ws._styles, 0, 1, 0, address._toCol);
+      while (cse2.Next()) {
+        for (int r = address._fromRow; r <= address._toRow; r++) {
+          if (!ws._styles.Exists(r, cse2.Column)) {
+            var s = cse2.Value;
+            if (styleCashe.ContainsKey(s)) {
+              ws.SetStyle(r, cse2.Column, styleCashe[s]);
+            } else {
+              ExcelXfs st = CellXfs[s];
+              int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+              styleCashe.Add(s, newId);
+              ws.SetStyle(r, cse2.Column, newId);
+            }
+          }
+        }
+      }
+    } else //Cellrange
+    {
+      for (int col = address.Start.Column; col <= address.End.Column; col++) {
+        for (int row = address.Start.Row; row <= address.End.Row; row++) {
+          var s = GetStyleId(ws, row, col);
+          if (styleCashe.ContainsKey(s)) {
+            ws.SetStyle(row, col, styleCashe[s]);
+          } else {
+            ExcelXfs st = CellXfs[s];
+            int newId = st.GetNewId(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+            styleCashe.Add(s, newId);
+            ws.SetStyle(row, col, newId);
+          }
+        }
+      }
+    }
   }
 
   internal int GetStyleId(ExcelWorksheet ws, int row, int col) {
@@ -195,6 +404,31 @@
     return 0;
   }
 
+  /// <summary>
+  /// Handles property changes on Named styles.
+  /// </summary>
+  /// <param name="sender"></param>
+  /// <param name="e"></param>
+  /// <returns></returns>
+  internal int NamedStylePropertyChange(StyleBase sender, StyleChangeEventArgs e) {
+    int index = NamedStyles.FindIndexById(e.Address);
+    if (index >= 0) {
+      int newId = CellStyleXfs[NamedStyles[index].StyleXfId]
+          .GetNewId(CellStyleXfs, sender, e.StyleClass, e.StyleProperty, e.Value);
+      int prevIx = NamedStyles[index].StyleXfId;
+      NamedStyles[index].StyleXfId = newId;
+      NamedStyles[index].Style.Index = newId;
+
+      NamedStyles[index].XfId = int.MinValue;
+      foreach (var style in CellXfs) {
+        if (style.XfId == prevIx) {
+          style.XfId = newId;
+        }
+      }
+    }
+    return 0;
+  }
+
   public ExcelStyleCollection<ExcelNumberFormatXml> NumberFormats = new();
   public ExcelStyleCollection<ExcelFontXml> Fonts = new();
   public ExcelStyleCollection<ExcelFillXml> Fills = new();
@@ -244,7 +478,7 @@
     CellStyleXfs[styleXfId].XfId = CellStyleXfs.Count - 1;
     int xfid = CloneStyle(styles, xfIdCopy, false, true); //Always add a new style (We create a new named style here)
     CellXfs[xfid].XfId = styleXfId;
-    style.Style = new(this, positionId, name, styleXfId);
+    style.Style = new(this, NamedStylePropertyChange, positionId, name, styleXfId);
     style.StyleXfId = styleXfId;
 
     style.Name = name;
diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs
index 33d2dd8..c561c33 100644
--- a/EPPlus/ExcelWorksheet.cs
+++ b/EPPlus/ExcelWorksheet.cs
@@ -1514,6 +1514,17 @@
     }
   }
 
+  /// <summary>
+  /// Printer settings
+  /// </summary>
+  public ExcelPrinterSettings PrinterSettings {
+    get {
+      var ps = new ExcelPrinterSettings(NameSpaceManager, TopNode, this);
+      ps.SchemaNodeOrder = SchemaNodeOrder;
+      return ps;
+    }
+  }
+
   ///// <summary>
   ///// Provides access to an individual cell within the worksheet.
   ///// </summary>
diff --git a/EPPlus/FormulaParsing/ExpressionGraph/CompileResult.cs b/EPPlus/FormulaParsing/ExpressionGraph/CompileResult.cs
index 90693ee..2545f6a 100644
--- a/EPPlus/FormulaParsing/ExpressionGraph/CompileResult.cs
+++ b/EPPlus/FormulaParsing/ExpressionGraph/CompileResult.cs
@@ -107,7 +107,9 @@
         || DataType == DataType.Boolean
         || DataType == DataType.Date;
 
-  public bool IsNumericString => DataType == DataType.String && ConvertUtil.IsNumericString(Result);
+  public bool IsNumericString {
+    get { return DataType == DataType.String && ConvertUtil.IsNumericString(Result); }
+  }
 
   public bool IsResultOfSubtotal { get; set; }
 
diff --git a/EPPlus/RangeCollection.cs b/EPPlus/RangeCollection.cs
index c7f8218..5dfb567 100644
--- a/EPPlus/RangeCollection.cs
+++ b/EPPlus/RangeCollection.cs
@@ -103,7 +103,9 @@
   /// <returns></returns>
   internal IRangeId this[int index] => _cells[_cellIndex[index].ListPointer];
 
-  internal int Count => _cells.Count;
+  internal int Count {
+    get { return _cells.Count; }
+  }
 
   internal void Add(IRangeId cell) {
     var ix = IndexOf(cell.RangeID);
diff --git a/EPPlus/Style/Dxf/DxfStyleBase.cs b/EPPlus/Style/Dxf/DxfStyleBase.cs
index 0c73cb0..7bbbcd0 100644
--- a/EPPlus/Style/Dxf/DxfStyleBase.cs
+++ b/EPPlus/Style/Dxf/DxfStyleBase.cs
@@ -21,8 +21,8 @@
 
   protected void SetValueColor(XmlHelper helper, string path, ExcelDxfColor color) {
     if (color != null && color.HasValue) {
-      if (color.Rgb != null) {
-        SetValue(helper, path + "/@rgb", color.Rgb);
+      if (color.Color != null) {
+        SetValue(helper, path + "/@rgb", color.Color.Value.ToArgb().ToString("x"));
       } else if (color.Auto != null) {
         SetValueBool(helper, path + "/@auto", color.Auto);
       } else if (color.Theme != null) {
@@ -68,7 +68,7 @@
     }
   }
 
-  protected string GetAsString(object v) {
+  protected internal string GetAsString(object v) {
     return (v ?? "").ToString();
   }
 
diff --git a/EPPlus/Style/Dxf/ExcelDxfColor.cs b/EPPlus/Style/Dxf/ExcelDxfColor.cs
index d22a7f9..4e6c4f9 100644
--- a/EPPlus/Style/Dxf/ExcelDxfColor.cs
+++ b/EPPlus/Style/Dxf/ExcelDxfColor.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Drawing;
 
 namespace OfficeOpenXml.Style.Dxf;
 
@@ -14,7 +15,7 @@
 
   public double? Tint { get; set; }
 
-  public string Rgb { get; set; }
+  public Color? Color { get; set; }
 
   protected internal override string Id =>
     GetAsString(Theme)
@@ -25,20 +26,20 @@
         + "|"
         + GetAsString(Tint)
         + "|"
-        + GetAsString(Rgb);
+        + GetAsString(Color == null ? "" : Color.Value.ToArgb().ToString("x"));
 
   protected internal override ExcelDxfColor Clone() {
     return new(_styles) {
       Theme = Theme,
       Index = Index,
-      Rgb = Rgb,
+      Color = Color,
       Auto = Auto,
       Tint = Tint,
     };
   }
 
   protected internal override bool HasValue =>
-    Theme != null || Index != null || Auto != null || Tint != null || Rgb != null;
+    Theme != null || Index != null || Auto != null || Tint != null || Color != null;
 
   protected internal override void CreateNodes(XmlHelper helper, string path) {
     throw new NotImplementedException();
diff --git a/EPPlus/Style/Dxf/ExcelDxfStyle.cs b/EPPlus/Style/Dxf/ExcelDxfStyle.cs
index 325d5b1..7180b21 100644
--- a/EPPlus/Style/Dxf/ExcelDxfStyle.cs
+++ b/EPPlus/Style/Dxf/ExcelDxfStyle.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Drawing;
 using System.Globalization;
 using System.Xml;
 
@@ -52,17 +53,46 @@
     return bi;
   }
 
-  private ExcelBorderStyle GetBorderStyleEnum(string style) =>
-    Enum.TryParse<ExcelBorderStyle>(style, true, out var result) ? result : ExcelBorderStyle.None;
+  private ExcelBorderStyle GetBorderStyleEnum(string style) {
+    if (style == "") {
+      return ExcelBorderStyle.None;
+    }
+    string sInStyle =
+        style.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture)
+            + style.Substring(1, style.Length - 1);
+    try {
+      return (ExcelBorderStyle)Enum.Parse(typeof(ExcelBorderStyle), sInStyle);
+    } catch {
+      return ExcelBorderStyle.None;
+    }
+  }
 
-  private ExcelFillStyle GetPatternTypeEnum(string patternType) =>
-    Enum.TryParse<ExcelFillStyle>(patternType, true, out var result) ? result : ExcelFillStyle.None;
+  private ExcelFillStyle GetPatternTypeEnum(string patternType) {
+    if (patternType == "") {
+      return ExcelFillStyle.None;
+    }
+    patternType =
+        patternType.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture)
+            + patternType.Substring(1, patternType.Length - 1);
+    try {
+      return (ExcelFillStyle)Enum.Parse(typeof(ExcelFillStyle), patternType);
+    } catch {
+      return ExcelFillStyle.None;
+    }
+  }
 
   private ExcelDxfColor GetColor(XmlHelperInstance helper, string path) {
     ExcelDxfColor ret = new ExcelDxfColor(_styles);
     ret.Theme = helper.GetXmlNodeIntNull(path + "/@theme");
     ret.Index = helper.GetXmlNodeIntNull(path + "/@indexed");
-    ret.Rgb = helper.GetXmlNodeString(path + "/@rgb");
+    string rgb = helper.GetXmlNodeString(path + "/@rgb");
+    if (rgb != "") {
+      ret.Color = Color.FromArgb(
+          int.Parse(rgb.Substring(0, 2), NumberStyles.AllowHexSpecifier),
+          int.Parse(rgb.Substring(2, 2), NumberStyles.AllowHexSpecifier),
+          int.Parse(rgb.Substring(4, 2), NumberStyles.AllowHexSpecifier),
+          int.Parse(rgb.Substring(6, 2), NumberStyles.AllowHexSpecifier));
+    }
     ret.Auto = helper.GetXmlNodeBoolNullable(path + "/@auto");
     ret.Tint = helper.GetXmlNodeDoubleNull(path + "/@tint");
     return ret;
diff --git a/EPPlus/Style/ExcelBorder.cs b/EPPlus/Style/ExcelBorder.cs
index 7784e20..bf07432 100644
--- a/EPPlus/Style/ExcelBorder.cs
+++ b/EPPlus/Style/ExcelBorder.cs
@@ -30,44 +30,53 @@
  * Jan Källman		License changed GPL-->LGPL 2011-12-16
  *******************************************************************************/
 
+using System.Drawing;
+
 namespace OfficeOpenXml.Style;
 
 /// <summary>
 /// Cell Border style
 /// </summary>
 public sealed class Border : StyleBase {
-  internal Border(ExcelStyles styles, int positionId, string address, int index)
-      : base(styles, positionId, address) {
+  internal Border(
+      ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
+      int positionId,
+      string address,
+      int index)
+      : base(styles, changedEvent, positionId, address) {
     Index = index;
   }
 
   /// <summary>
   /// Left border style
   /// </summary>
-  public ExcelBorderItem Left => new(_styles, _positionID, _address, eStyleClass.BorderLeft, this);
+  public ExcelBorderItem Left =>
+    new(_styles, _ChangedEvent, _positionID, _address, eStyleClass.BorderLeft, this);
 
   /// <summary>
   /// Right border style
   /// </summary>
   public ExcelBorderItem Right =>
-    new(_styles, _positionID, _address, eStyleClass.BorderRight, this);
+    new(_styles, _ChangedEvent, _positionID, _address, eStyleClass.BorderRight, this);
 
   /// <summary>
   /// Top border style
   /// </summary>
-  public ExcelBorderItem Top => new(_styles, _positionID, _address, eStyleClass.BorderTop, this);
+  public ExcelBorderItem Top =>
+    new(_styles, _ChangedEvent, _positionID, _address, eStyleClass.BorderTop, this);
 
   /// <summary>
   /// Bottom border style
   /// </summary>
   public ExcelBorderItem Bottom =>
-    new(_styles, _positionID, _address, eStyleClass.BorderBottom, this);
+    new(_styles, _ChangedEvent, _positionID, _address, eStyleClass.BorderBottom, this);
 
   /// <summary>
   /// 0Diagonal border style
   /// </summary>
   public ExcelBorderItem Diagonal =>
-    new(_styles, _positionID, _address, eStyleClass.BorderDiagonal, this);
+    new(_styles, _ChangedEvent, _positionID, _address, eStyleClass.BorderDiagonal, this);
 
   /// <summary>
   /// A diagonal from the bottom left to top right of the cell
@@ -79,6 +88,10 @@
       }
       return false;
     }
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Border, eStyleProperty.BorderDiagonalUp, value, _positionID, _address));
   }
 
   /// <summary>
@@ -91,8 +104,99 @@
       }
       return false;
     }
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Border, eStyleProperty.BorderDiagonalDown, value, _positionID, _address));
   }
 
   internal override string Id =>
     Top.Id + Bottom.Id + Left.Id + Right.Id + Diagonal.Id + DiagonalUp + DiagonalDown;
+
+  /// <summary>
+  /// Set the border style around the range.
+  /// </summary>
+  /// <param name="style">The border style</param>
+  public void BorderAround(ExcelBorderStyle style) {
+    var addr = new ExcelAddress(_address);
+    SetBorderAroundStyle(style, addr);
+  }
+
+  /// <summary>
+  /// Set the border style around the range.
+  /// </summary>
+  /// <param name="style">The border style</param>
+  /// <param name="color">The color of the border</param>
+  public void BorderAround(ExcelBorderStyle style, Color color) {
+    var addr = new ExcelAddress(_address);
+    SetBorderAroundStyle(style, addr);
+
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderTop,
+            eStyleProperty.Color,
+            color.ToArgb().ToString("X"),
+            _positionID,
+            new ExcelAddress(addr._fromRow, addr._fromCol, addr._fromRow, addr._toCol).Address));
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderBottom,
+            eStyleProperty.Color,
+            color.ToArgb().ToString("X"),
+            _positionID,
+            new ExcelAddress(addr._toRow, addr._fromCol, addr._toRow, addr._toCol).Address));
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderLeft,
+            eStyleProperty.Color,
+            color.ToArgb().ToString("X"),
+            _positionID,
+            new ExcelAddress(addr._fromRow, addr._fromCol, addr._toRow, addr._fromCol).Address));
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderRight,
+            eStyleProperty.Color,
+            color.ToArgb().ToString("X"),
+            _positionID,
+            new ExcelAddress(addr._fromRow, addr._toCol, addr._toRow, addr._toCol).Address));
+  }
+
+  private void SetBorderAroundStyle(ExcelBorderStyle style, ExcelAddress addr) {
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderTop,
+            eStyleProperty.Style,
+            style,
+            _positionID,
+            new ExcelAddress(addr._fromRow, addr._fromCol, addr._fromRow, addr._toCol).Address));
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderBottom,
+            eStyleProperty.Style,
+            style,
+            _positionID,
+            new ExcelAddress(addr._toRow, addr._fromCol, addr._toRow, addr._toCol).Address));
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderLeft,
+            eStyleProperty.Style,
+            style,
+            _positionID,
+            new ExcelAddress(addr._fromRow, addr._fromCol, addr._toRow, addr._fromCol).Address));
+    _ChangedEvent(
+        this,
+        new(
+            eStyleClass.BorderRight,
+            eStyleProperty.Style,
+            style,
+            _positionID,
+            new ExcelAddress(addr._fromRow, addr._toCol, addr._toRow, addr._toCol).Address));
+  }
 }
diff --git a/EPPlus/Style/ExcelBorderItem.cs b/EPPlus/Style/ExcelBorderItem.cs
index c8922d2..7708ab9 100644
--- a/EPPlus/Style/ExcelBorderItem.cs
+++ b/EPPlus/Style/ExcelBorderItem.cs
@@ -44,11 +44,12 @@
 
   internal ExcelBorderItem(
       ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
       int worksheetId,
       string address,
       eStyleClass cls,
       StyleBase parent)
-      : base(styles, worksheetId, address) {
+      : base(styles, changedEvent, worksheetId, address) {
     _cls = cls;
     _parent = parent;
   }
@@ -56,7 +57,10 @@
   /// <summary>
   /// The line style of the border
   /// </summary>
-  public ExcelBorderStyle Style => GetSource().Style;
+  public ExcelBorderStyle Style {
+    get => GetSource().Style;
+    set => _ChangedEvent(this, new(_cls, eStyleProperty.Style, value, _positionID, _address));
+  }
 
   private ExcelColor _color;
 
@@ -66,7 +70,7 @@
   public ExcelColor Color {
     get {
       if (_color == null) {
-        _color = new(_styles, _positionID, _address, _cls, _parent);
+        _color = new(_styles, _ChangedEvent, _positionID, _address, _cls, _parent);
       }
       return _color;
     }
diff --git a/EPPlus/Style/ExcelColor.cs b/EPPlus/Style/ExcelColor.cs
index 2050f50..e673510 100644
--- a/EPPlus/Style/ExcelColor.cs
+++ b/EPPlus/Style/ExcelColor.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using OfficeOpenXml.Style.XmlAccess;
 
 namespace OfficeOpenXml.Style;
@@ -44,11 +45,12 @@
 
   internal ExcelColor(
       ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
       int worksheetId,
       string address,
       eStyleClass cls,
       StyleBase parent)
-      : base(styles, worksheetId, address) {
+      : base(styles, changedEvent, worksheetId, address) {
     _parent = parent;
     _cls = cls;
   }
@@ -61,17 +63,41 @@
   /// <summary>
   /// The tint value
   /// </summary>
-  public decimal Tint => GetSource().Tint;
+  public decimal Tint {
+    get => GetSource().Tint;
+    set {
+      if (value > 1 || value < -1) {
+        throw (new ArgumentOutOfRangeException("Value must be between -1 and 1"));
+      }
+      _ChangedEvent(this, new(_cls, eStyleProperty.Tint, value, _positionID, _address));
+    }
+  }
 
   /// <summary>
   /// The RGB value
   /// </summary>
-  public string Rgb => GetSource().Rgb;
+  public string Rgb {
+    get => GetSource().Rgb;
+    internal set =>
+      _ChangedEvent(this, new(_cls, eStyleProperty.Color, value, _positionID, _address));
+  }
 
   /// <summary>
   /// The indexed color number.
   /// </summary>
-  public int Indexed => GetSource().Indexed;
+  public int Indexed {
+    get => GetSource().Indexed;
+    set =>
+      _ChangedEvent(this, new(_cls, eStyleProperty.IndexedColor, value, _positionID, _address));
+  }
+
+  /// <summary>
+  /// Set the color of the object
+  /// </summary>
+  /// <param name="color">The color</param>
+  public void SetColor(Color color) {
+    Rgb = color.ToArgb().ToString("X");
+  }
 
   internal override string Id => Theme + Tint + Rgb + Indexed;
 
diff --git a/EPPlus/Style/ExcelFill.cs b/EPPlus/Style/ExcelFill.cs
index e6cc1b2..d1a5ea6 100644
--- a/EPPlus/Style/ExcelFill.cs
+++ b/EPPlus/Style/ExcelFill.cs
@@ -36,8 +36,13 @@
 /// The background fill of a cell
 /// </summary>
 public class ExcelFill : StyleBase {
-  internal ExcelFill(ExcelStyles styles, int positionId, string address, int index)
-      : base(styles, positionId, address) {
+  internal ExcelFill(
+      ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
+      int positionId,
+      string address,
+      int index)
+      : base(styles, changedEvent, positionId, address) {
     Index = index;
   }
 
@@ -51,6 +56,14 @@
       }
       return _styles.Fills[Index].PatternType;
     }
+    set {
+      if (_gradient != null) {
+        _gradient = null;
+      }
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Fill, eStyleProperty.PatternType, value, _positionID, _address));
+    }
   }
 
   private ExcelColor _patternColor;
@@ -61,7 +74,13 @@
   public ExcelColor PatternColor {
     get {
       if (_patternColor == null) {
-        _patternColor = new(_styles, _positionID, _address, eStyleClass.FillPatternColor, this);
+        _patternColor = new(
+            _styles,
+            _ChangedEvent,
+            _positionID,
+            _address,
+            eStyleClass.FillPatternColor,
+            this);
         if (_gradient != null) {
           _gradient = null;
         }
@@ -80,6 +99,7 @@
       if (_backgroundColor == null) {
         _backgroundColor = new(
             _styles,
+            _ChangedEvent,
             _positionID,
             _address,
             eStyleClass.FillBackgroundColor,
@@ -100,7 +120,7 @@
   public ExcelGradientFill Gradient {
     get {
       if (_gradient == null) {
-        _gradient = new(_styles, _positionID, _address, Index);
+        _gradient = new(_styles, _ChangedEvent, _positionID, _address, Index);
         _backgroundColor = null;
         _patternColor = null;
       }
diff --git a/EPPlus/Style/ExcelFont.cs b/EPPlus/Style/ExcelFont.cs
index 2cb6d5c..36beb83 100644
--- a/EPPlus/Style/ExcelFont.cs
+++ b/EPPlus/Style/ExcelFont.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 
 namespace OfficeOpenXml.Style;
 
@@ -38,68 +39,146 @@
 /// Cell style Font
 /// </summary>
 public sealed class ExcelFont : StyleBase {
-  internal ExcelFont(ExcelStyles styles, int positionId, string address, int index)
-      : base(styles, positionId, address) {
+  internal ExcelFont(
+      ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
+      int positionId,
+      string address,
+      int index)
+      : base(styles, changedEvent, positionId, address) {
     Index = index;
   }
 
   /// <summary>
   /// The name of the font
   /// </summary>
-  public string Name => _styles.Fonts[Index].Name;
+  public string Name {
+    get => _styles.Fonts[Index].Name;
+    set =>
+      _ChangedEvent(this, new(eStyleClass.Font, eStyleProperty.Name, value, _positionID, _address));
+  }
 
   /// <summary>
   /// The Size of the font
   /// </summary>
-  public float Size => _styles.Fonts[Index].Size;
+  public float Size {
+    get => _styles.Fonts[Index].Size;
+    set =>
+      _ChangedEvent(this, new(eStyleClass.Font, eStyleProperty.Size, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Font family
   /// </summary>
-  public int Family => _styles.Fonts[Index].Family;
+  public int Family {
+    get => _styles.Fonts[Index].Family;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Font, eStyleProperty.Family, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Cell color
   /// </summary>
-  public ExcelColor Color => new(_styles, _positionID, _address, eStyleClass.Font, this);
+  public ExcelColor Color =>
+    new(_styles, _ChangedEvent, _positionID, _address, eStyleClass.Font, this);
 
   /// <summary>
   /// Scheme
   /// </summary>
-  public string Scheme => _styles.Fonts[Index].Scheme;
+  public string Scheme {
+    get => _styles.Fonts[Index].Scheme;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Font, eStyleProperty.Scheme, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Font-bold
   /// </summary>
-  public bool Bold => _styles.Fonts[Index].Bold;
+  public bool Bold {
+    get => _styles.Fonts[Index].Bold;
+    set =>
+      _ChangedEvent(this, new(eStyleClass.Font, eStyleProperty.Bold, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Font-italic
   /// </summary>
-  public bool Italic => _styles.Fonts[Index].Italic;
+  public bool Italic {
+    get => _styles.Fonts[Index].Italic;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Font, eStyleProperty.Italic, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Font-Strikeout
   /// </summary>
-  public bool Strike => _styles.Fonts[Index].Strike;
+  public bool Strike {
+    get => _styles.Fonts[Index].Strike;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Font, eStyleProperty.Strike, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Font-Underline
   /// </summary>
-  public bool UnderLine => _styles.Fonts[Index].UnderLine;
+  public bool UnderLine {
+    get => _styles.Fonts[Index].UnderLine;
+    set {
+      if (value) {
+        UnderLineType = ExcelUnderLineType.Single;
+      } else {
+        UnderLineType = ExcelUnderLineType.None;
+      }
+      //_ChangedEvent(this, new StyleChangeEventArgs(eStyleClass.Font, eStyleProperty.UnderlineType, value, _positionID, _address));
+    }
+  }
 
-  public ExcelUnderLineType UnderLineType => _styles.Fonts[Index].UnderLineType;
+  public ExcelUnderLineType UnderLineType {
+    get => _styles.Fonts[Index].UnderLineType;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Font, eStyleProperty.UnderlineType, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Font-Vertical Align
   /// </summary>
-  public ExcelVerticalAlignmentFont VerticalAlign =>
-    Enum.TryParse<ExcelVerticalAlignmentFont>(
-        _styles.Fonts[Index].VerticalAlign,
-        true,
-        out var result)
-        ? result
-        : ExcelVerticalAlignmentFont.None;
+  public ExcelVerticalAlignmentFont VerticalAlign {
+    get {
+      if (_styles.Fonts[Index].VerticalAlign == "") {
+        return ExcelVerticalAlignmentFont.None;
+      }
+      return (ExcelVerticalAlignmentFont)
+        Enum.Parse(typeof(ExcelVerticalAlignmentFont), _styles.Fonts[Index].VerticalAlign, true);
+    }
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Font, eStyleProperty.VerticalAlign, value, _positionID, _address));
+  }
+
+  /// <summary>
+  /// Set the font from a Font object
+  /// </summary>
+  /// <param name="font"></param>
+  public void SetFromFont(Font font) {
+    Name = font.Name;
+    //Family=fnt.FontFamily.;
+    Size = (int)font.Size;
+    Strike = font.Strikeout;
+    Bold = font.Bold;
+    UnderLine = font.Underline;
+    Italic = font.Italic;
+  }
 
   internal override string Id =>
     Name
diff --git a/EPPlus/Style/ExcelGradientFill.cs b/EPPlus/Style/ExcelGradientFill.cs
index 508a1f7..16e73c8 100644
--- a/EPPlus/Style/ExcelGradientFill.cs
+++ b/EPPlus/Style/ExcelGradientFill.cs
@@ -39,40 +39,112 @@
 /// The background fill of a cell
 /// </summary>
 public class ExcelGradientFill : StyleBase {
-  internal ExcelGradientFill(ExcelStyles styles, int positionId, string address, int index)
-      : base(styles, positionId, address) {
+  internal ExcelGradientFill(
+      ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
+      int positionId,
+      string address,
+      int index)
+      : base(styles, changedEvent, positionId, address) {
     Index = index;
   }
 
   /// <summary>
   /// Angle of the linear gradient
   /// </summary>
-  public double Degree => ((ExcelGradientFillXml)_styles.Fills[Index]).Degree;
+  public double Degree {
+    get => ((ExcelGradientFillXml)_styles.Fills[Index]).Degree;
+    set =>
+      _ChangedEvent(
+          this,
+          new(
+              eStyleClass.GradientFill,
+              eStyleProperty.GradientDegree,
+              value,
+              _positionID,
+              _address));
+  }
 
   /// <summary>
   /// Linear or Path gradient
   /// </summary>
-  public ExcelFillGradientType Type => ((ExcelGradientFillXml)_styles.Fills[Index]).Type;
+  public ExcelFillGradientType Type {
+    get => ((ExcelGradientFillXml)_styles.Fills[Index]).Type;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.GradientFill, eStyleProperty.GradientType, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Specifies in percentage format(from the top to the bottom) the position of the top edge of the inner rectangle (color 1). For top, 0 means the top edge of the inner rectangle is on the top edge of the cell, and 1 means it is on the bottom edge of the cell. (applies to From Corner and From Center gradients).
   /// </summary>
-  public double Top => ((ExcelGradientFillXml)_styles.Fills[Index]).Top;
+  public double Top {
+    get => ((ExcelGradientFillXml)_styles.Fills[Index]).Top;
+    set {
+      if (value < 0 | value > 1) {
+        throw (new ArgumentOutOfRangeException("Value must be between 0 and 1"));
+      }
+      _ChangedEvent(
+          this,
+          new(eStyleClass.GradientFill, eStyleProperty.GradientTop, value, _positionID, _address));
+    }
+  }
 
   /// <summary>
   /// Specifies in percentage format (from the top to the bottom) the position of the bottom edge of the inner rectangle (color 1). For bottom, 0 means the bottom edge of the inner rectangle is on the top edge of the cell, and 1 means it is on the bottom edge of the cell.
   /// </summary>
-  public double Bottom => ((ExcelGradientFillXml)_styles.Fills[Index]).Bottom;
+  public double Bottom {
+    get => ((ExcelGradientFillXml)_styles.Fills[Index]).Bottom;
+    set {
+      if (value < 0 | value > 1) {
+        throw (new ArgumentOutOfRangeException("Value must be between 0 and 1"));
+      }
+      _ChangedEvent(
+          this,
+          new(
+              eStyleClass.GradientFill,
+              eStyleProperty.GradientBottom,
+              value,
+              _positionID,
+              _address));
+    }
+  }
 
   /// <summary>
   /// Specifies in percentage format (from the left to the right) the position of the left edge of the inner rectangle (color 1). For left, 0 means the left edge of the inner rectangle is on the left edge of the cell, and 1 means it is on the right edge of the cell. (applies to From Corner and From Center gradients).
   /// </summary>
-  public double Left => ((ExcelGradientFillXml)_styles.Fills[Index]).Left;
+  public double Left {
+    get => ((ExcelGradientFillXml)_styles.Fills[Index]).Left;
+    set {
+      if (value < 0 | value > 1) {
+        throw (new ArgumentOutOfRangeException("Value must be between 0 and 1"));
+      }
+      _ChangedEvent(
+          this,
+          new(eStyleClass.GradientFill, eStyleProperty.GradientLeft, value, _positionID, _address));
+    }
+  }
 
   /// <summary>
   /// Specifies in percentage format (from the left to the right) the position of the right edge of the inner rectangle (color 1). For right, 0 means the right edge of the inner rectangle is on the left edge of the cell, and 1 means it is on the right edge of the cell. (applies to From Corner and From Center gradients).
   /// </summary>
-  public double Right => ((ExcelGradientFillXml)_styles.Fills[Index]).Right;
+  public double Right {
+    get => ((ExcelGradientFillXml)_styles.Fills[Index]).Right;
+    set {
+      if (value < 0 | value > 1) {
+        throw (new ArgumentOutOfRangeException("Value must be between 0 and 1"));
+      }
+      _ChangedEvent(
+          this,
+          new(
+              eStyleClass.GradientFill,
+              eStyleProperty.GradientRight,
+              value,
+              _positionID,
+              _address));
+    }
+  }
 
   private ExcelColor _gradientColor1;
 
@@ -82,7 +154,13 @@
   public ExcelColor Color1 {
     get {
       if (_gradientColor1 == null) {
-        _gradientColor1 = new(_styles, _positionID, _address, eStyleClass.FillGradientColor1, this);
+        _gradientColor1 = new(
+            _styles,
+            _ChangedEvent,
+            _positionID,
+            _address,
+            eStyleClass.FillGradientColor1,
+            this);
       }
       return _gradientColor1;
     }
@@ -96,7 +174,13 @@
   public ExcelColor Color2 {
     get {
       if (_gradientColor2 == null) {
-        _gradientColor2 = new(_styles, _positionID, _address, eStyleClass.FillGradientColor2, this);
+        _gradientColor2 = new(
+            _styles,
+            _ChangedEvent,
+            _positionID,
+            _address,
+            eStyleClass.FillGradientColor2,
+            this);
       }
       return _gradientColor2;
     }
diff --git a/EPPlus/Style/ExcelNumberFormat.cs b/EPPlus/Style/ExcelNumberFormat.cs
index b971e7c..d1841df 100644
--- a/EPPlus/Style/ExcelNumberFormat.cs
+++ b/EPPlus/Style/ExcelNumberFormat.cs
@@ -36,8 +36,13 @@
 /// The numberformat of the cell
 /// </summary>
 public sealed class ExcelNumberFormat : StyleBase {
-  internal ExcelNumberFormat(ExcelStyles styles, int positionId, string address, int index)
-      : base(styles, positionId, address) {
+  internal ExcelNumberFormat(
+      ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
+      int positionId,
+      string address,
+      int index)
+      : base(styles, changedEvent, positionId, address) {
     Index = index;
   }
 
@@ -46,6 +51,10 @@
   /// </summary>
   public int NumFmtID => Index;
 
+  //set
+  //{
+  //    _ChangedEvent(this, new StyleChangeEventArgs(eStyleClass.Numberformat, "NumFmtID", value, _workSheetID, _address));
+  //}
   /// <summary>
   /// The numberformat
   /// </summary>
@@ -58,6 +67,15 @@
       }
       return "general";
     }
+    set =>
+      _ChangedEvent(
+          this,
+          new(
+              eStyleClass.Numberformat,
+              eStyleProperty.Format,
+              (string.IsNullOrEmpty(value) ? "General" : value),
+              _positionID,
+              _address));
   }
 
   internal override string Id => Format;
diff --git a/EPPlus/Style/ExcelRichText.cs b/EPPlus/Style/ExcelRichText.cs
index 969dccc..2207656 100644
--- a/EPPlus/Style/ExcelRichText.cs
+++ b/EPPlus/Style/ExcelRichText.cs
@@ -33,6 +33,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using System.Globalization;
 using System.Xml;
 
@@ -215,13 +216,33 @@
   /// <summary>
   /// Vertical Alignment
   /// </summary>
-  public ExcelVerticalAlignmentFont VerticalAlign =>
-    Enum.TryParse<ExcelVerticalAlignmentFont>(
-        GetXmlNodeString(TopNode, _vertAlignPath),
-        true,
-        out var result)
-        ? result
-        : ExcelVerticalAlignmentFont.None;
+  public ExcelVerticalAlignmentFont VerticalAlign {
+    get {
+      string v = GetXmlNodeString(_vertAlignPath);
+      if (v == "") {
+        return ExcelVerticalAlignmentFont.None;
+      }
+      try {
+        return (ExcelVerticalAlignmentFont)Enum.Parse(typeof(ExcelVerticalAlignmentFont), v, true);
+      } catch {
+        return ExcelVerticalAlignmentFont.None;
+      }
+    }
+    set {
+      _collection.ConvertRichtext();
+      if (value == ExcelVerticalAlignmentFont.None) {
+        // If Excel 2010 encounters a vertical align value of blank, it will not load
+        // the spreadsheet. So if None is specified, delete the node, it will be
+        // recreated if a new value is applied later.
+        DeleteNode(_vertAlignPath);
+      } else {
+        SetXmlNodeString(_vertAlignPath, value.ToString().ToLowerInvariant());
+      }
+      if (_callback != null) {
+        _callback();
+      }
+    }
+  }
 
   private const string _sizePath = "d:rPr/d:sz/@val";
 
@@ -257,5 +278,30 @@
 
   private const string _colorPath = "d:rPr/d:color/@rgb";
 
+  /// <summary>
+  /// Text color
+  /// </summary>
+  public Color Color {
+    get {
+      string col = GetXmlNodeString(_colorPath);
+      if (col == "") {
+        return Color.Empty;
+      }
+      return Color.FromArgb(int.Parse(col, NumberStyles.AllowHexSpecifier));
+    }
+    set {
+      _collection.ConvertRichtext();
+      SetXmlNodeString(
+          _colorPath,
+          value
+              .ToArgb()
+              .ToString(
+                  "X") /*.Substring(2, 6)*/);
+      if (_callback != null) {
+        _callback();
+      }
+    }
+  }
+
   public ExcelRichTextCollection _collection { get; set; }
 }
diff --git a/EPPlus/Style/ExcelRichTextCollection.cs b/EPPlus/Style/ExcelRichTextCollection.cs
index 7cd4867..5e87c4b 100644
--- a/EPPlus/Style/ExcelRichTextCollection.cs
+++ b/EPPlus/Style/ExcelRichTextCollection.cs
@@ -32,6 +32,7 @@
 
 using System.Collections;
 using System.Collections.Generic;
+using System.Drawing;
 using System.Globalization;
 using System.Linq;
 using System.Text;
@@ -81,6 +82,56 @@
   /// </summary>
   public int Count => _list.Count;
 
+  /// <summary>
+  /// Add a rich text string
+  /// </summary>
+  /// <param name="text">The text to add</param>
+  /// <returns></returns>
+  public ExcelRichText Add(string text) {
+    ConvertRichtext();
+    XmlDocument doc;
+    if (TopNode is XmlDocument document) {
+      doc = document;
+    } else {
+      doc = TopNode.OwnerDocument;
+    }
+    var node = doc.CreateElement("d", "r", ExcelPackage._schemaMain);
+    TopNode.AppendChild(node);
+    var rt = new ExcelRichText(NameSpaceManager, node, this);
+    if (_list.Count > 0) {
+      ExcelRichText prevItem = _list[_list.Count - 1];
+      rt.FontName = prevItem.FontName;
+      rt.Size = prevItem.Size;
+      if (prevItem.Color.IsEmpty) {
+        rt.Color = Color.Black;
+      } else {
+        rt.Color = prevItem.Color;
+      }
+      rt.PreserveSpace = rt.PreserveSpace;
+      rt.Bold = prevItem.Bold;
+      rt.Italic = prevItem.Italic;
+      rt.UnderLine = prevItem.UnderLine;
+    } else if (_cells == null) {
+      rt.FontName = "Calibri";
+      rt.Size = 11;
+    } else {
+      var style = _cells.Offset(0, 0).Style;
+      rt.FontName = style.Font.Name;
+      rt.Size = style.Font.Size;
+      rt.Bold = style.Font.Bold;
+      rt.Italic = style.Font.Italic;
+      _cells.IsRichText = true;
+    }
+    rt.Text = text;
+    rt.PreserveSpace = true;
+    if (_cells != null) {
+      rt.SetCallback(UpdateCells);
+      UpdateCells();
+    }
+    _list.Add(rt);
+    return rt;
+  }
+
   internal void ConvertRichtext() {
     if (_cells == null) {
       return;
@@ -109,6 +160,11 @@
       this[0].Italic = fnt.Italic;
       this[0].Size = fnt.Size;
       this[0].UnderLine = fnt.UnderLine;
+
+      if (fnt.Color.Rgb != ""
+          && int.TryParse(fnt.Color.Rgb, NumberStyles.HexNumber, null, out var hex)) {
+        this[0].Color = Color.FromArgb(hex);
+      }
     }
   }
 
@@ -117,6 +173,18 @@
   }
 
   /// <summary>
+  /// Clear the collection
+  /// </summary>
+  public void Clear() {
+    _list.Clear();
+    TopNode.RemoveAll();
+    UpdateCells();
+    if (_cells != null) {
+      _cells.IsRichText = false;
+    }
+  }
+
+  /// <summary>
   /// Removes an item at the specific index
   /// </summary>
   /// <param name="index"></param>
@@ -156,6 +224,16 @@
       }
       return sb.ToString();
     }
+    set {
+      if (Count == 0) {
+        Add(value);
+      } else {
+        this[0].Text = value;
+        for (int ix = 1; ix < Count; ix++) {
+          RemoveAt(ix);
+        }
+      }
+    }
   }
 
   IEnumerator<ExcelRichText> IEnumerable<ExcelRichText>.GetEnumerator() {
diff --git a/EPPlus/Style/ExcelRichTextHtmlUtility.cs b/EPPlus/Style/ExcelRichTextHtmlUtility.cs
new file mode 100644
index 0000000..83e37c6
--- /dev/null
+++ b/EPPlus/Style/ExcelRichTextHtmlUtility.cs
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * You may amend and distribute as you like, but don't remove this header!
+ *
+ * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
+ * See http://www.codeplex.com/EPPlus for details.
+ *
+ * Copyright (C) 2011  Jan Källman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
+ * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
+ *
+ * All code and executables are provided "as is" with no warranty either express or implied.
+ * The author accepts no liability for any damage or loss of business that this product may cause.
+ *
+ * Code change notes:
+ *
+ * Author				Change						Date
+ * ******************************************************************************
+ * Richard Tallent		Initial Release				2012-08-13
+ *******************************************************************************/
+
+using System;
+using System.Text.RegularExpressions;
+using System.Web;
+
+namespace OfficeOpenXml.Style;
+
+public class ExcelRichTextHtmlUtility {
+  /// <summary>
+  /// Provides basic HTML support by converting well-behaved HTML into appropriate RichText blocks.
+  /// HTML support is limited, and does not include font colors, sizes, or typefaces at this time,
+  /// and also does not support CSS style attributes. It does support line breaks using the BR tag.
+  ///
+  /// This routine parses the HTML into RegEx pairings of an HTML tag and the text until the NEXT
+  /// tag (if any). The tag is parsed to determine the setting change to be applied to the last set
+  /// of settings, and if the text is not blank, a new block is added to rich text.
+  /// </summary>
+  /// <param name="range"></param>
+  /// <param name="html">The HTML to parse into RichText</param>
+  /// <param name="defaultFontName"></param>
+  /// <param name="defaultFontSize"></param>
+  public static void SetRichTextFromHtml(
+      ExcelRange range,
+      string html,
+      string defaultFontName,
+      short defaultFontSize) {
+    // Reset the cell value, just in case there is an existing RichText value.
+    range.Value = "";
+
+    // Sanity check for blank values, skips creating Regex objects for performance.
+    if (String.IsNullOrEmpty(html)) {
+      range.IsRichText = false;
+      return;
+    }
+
+    // Change all BR tags to line breaks. http://epplus.codeplex.com/discussions/238692/
+    // Cells with line breaks aren't necessarily considered rich text, so this is performed
+    // before parsing the HTML tags.
+    html = Regex.Replace(
+        html,
+        "<br[^>]*>",
+        "\r\n",
+        RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+    string tag;
+    string text;
+    ExcelRichText thisrt = null;
+    bool isFirst = true;
+
+    // Get all pairs of legitimate tags and the text between them. This loop will
+    // only execute if there is at least one start or end tag.
+    foreach (Match m in Regex.Matches(
+        html,
+        @"<(/?[a-z]+)[^<>]*>([\s\S]*?)(?=</?[a-z]+[^<>]*>|$)",
+        RegexOptions.Compiled | RegexOptions.IgnoreCase)) {
+      if (isFirst) {
+        // On the very first match, set up the initial rich text object with
+        // the defaults for the text BEFORE the match.
+        range.IsRichText = true;
+        thisrt = range.RichText.Add(CleanText(html.Substring(0, m.Index))); // May be 0-length
+        thisrt.Size = defaultFontSize; // Set the default font size
+        thisrt.FontName = defaultFontName; // Set the default font name
+        isFirst = false;
+      }
+      // Get the tag and the block of text until the NEXT tag or EOS. If there are HTML entities
+      // encoded, unencode them, they should be passed to RichText as normal characters (other
+      // than non-breaking spaces, which should be replaced with normal spaces, they break Excel.
+      tag = m.Groups[1].Captures[0].Value;
+      text = CleanText(m.Groups[2].Captures[0].Value);
+
+      if (thisrt.Text == "") {
+        // The most recent rich text block wasn't *actually* used last time around, so update
+        // the text and keep it as the "current" block. This happens with the first block if
+        // it starts with a tag, and may happen later if tags come one right after the other.
+        thisrt.Text = text;
+      } else {
+        // The current rich text block has some text, so create a new one. RichText.Add()
+        // automatically applies the settings from the previous block, other than vertical
+        // alignment.
+        thisrt = range.RichText.Add(text);
+      }
+      // Override the settings based on the current tag, keep all other settings.
+      SetStyleFromTag(tag, thisrt);
+    }
+
+    if (thisrt == null) {
+      // No HTML tags were found, so treat this as a normal text value.
+      range.IsRichText = false;
+      range.Value = CleanText(html);
+    } else if (String.IsNullOrEmpty(thisrt.Text)) {
+      // Rich text was found, but the last node contains no text, so remove it. This can happen if,
+      // say, the end of the string is an end tag or unsupported tag (common).
+      range.RichText.Remove(thisrt);
+
+      // Failsafe -- the HTML may be just tags, no text, in which case there may be no rich text
+      // directives that remain. If that is the case, turn off rich text and treat this like a blank
+      // cell value.
+      if (range.RichText.Count == 0) {
+        range.IsRichText = false;
+        range.Value = "";
+      }
+    }
+  }
+
+  private static void SetStyleFromTag(string tag, ExcelRichText settings) {
+    switch (tag.ToLower()) {
+      case "b":
+      case "strong":
+        settings.Bold = true;
+        break;
+      case "i":
+      case "em":
+        settings.Italic = true;
+        break;
+      case "u":
+        settings.UnderLine = true;
+        break;
+      case "s":
+      case "strike":
+        settings.Strike = true;
+        break;
+      case "sup":
+        settings.VerticalAlign = ExcelVerticalAlignmentFont.Superscript;
+        break;
+      case "sub":
+        settings.VerticalAlign = ExcelVerticalAlignmentFont.Subscript;
+        break;
+      case "/b":
+      case "/strong":
+        settings.Bold = false;
+        break;
+      case "/i":
+      case "/em":
+        settings.Italic = false;
+        break;
+      case "/u":
+        settings.UnderLine = false;
+        break;
+      case "/s":
+      case "/strike":
+        settings.Strike = false;
+        break;
+      case "/sup":
+      case "/sub":
+        settings.VerticalAlign = ExcelVerticalAlignmentFont.None;
+        break;
+    }
+  }
+
+  private static string CleanText(string s) {
+    // Need to convert HTML entities (named or numbered) into actual Unicode characters
+    s = HttpUtility.HtmlDecode(s);
+    // Remove any non-breaking spaces, kills Excel
+    s = s.Replace("\u00A0", " ");
+    return s;
+  }
+}
diff --git a/EPPlus/Style/ExcelStyle.cs b/EPPlus/Style/ExcelStyle.cs
index f7b60f9..9c2509a 100644
--- a/EPPlus/Style/ExcelStyle.cs
+++ b/EPPlus/Style/ExcelStyle.cs
@@ -39,8 +39,13 @@
 /// Toplevel class for cell styling
 /// </summary>
 public sealed class ExcelStyle : StyleBase {
-  internal ExcelStyle(ExcelStyles styles, int positionId, string address, int xfsId)
-      : base(styles, positionId, address) {
+  internal ExcelStyle(
+      ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
+      int positionId,
+      string address,
+      int xfsId)
+      : base(styles, changedEvent, positionId, address) {
     Index = xfsId;
     ExcelXfs xfs;
     if (positionId > -1) {
@@ -50,10 +55,10 @@
     }
     Styles = styles;
     PositionID = positionId;
-    Numberformat = new(styles, PositionID, address, xfs.NumberFormatId);
-    Font = new(styles, PositionID, address, xfs.FontId);
-    Fill = new(styles, PositionID, address, xfs.FillId);
-    Border = new(styles, PositionID, address, xfs.BorderId);
+    Numberformat = new(styles, changedEvent, PositionID, address, xfs.NumberFormatId);
+    Font = new(styles, changedEvent, PositionID, address, xfs.FontId);
+    Fill = new(styles, changedEvent, PositionID, address, xfs.FillId);
+    Border = new(styles, changedEvent, PositionID, address, xfs.BorderId);
   }
 
   /// <summary>
@@ -79,54 +84,122 @@
   /// <summary>
   /// The horizontal alignment in the cell
   /// </summary>
-  public ExcelHorizontalAlignment HorizontalAlignment => _styles.CellXfs[Index].HorizontalAlignment;
+  public ExcelHorizontalAlignment HorizontalAlignment {
+    get => _styles.CellXfs[Index].HorizontalAlignment;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.HorizontalAlign, value, _positionID, _address));
+  }
 
   /// <summary>
   /// The vertical alignment in the cell
   /// </summary>
-  public ExcelVerticalAlignment VerticalAlignment => _styles.CellXfs[Index].VerticalAlignment;
+  public ExcelVerticalAlignment VerticalAlignment {
+    get => _styles.CellXfs[Index].VerticalAlignment;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.VerticalAlign, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Wrap the text
   /// </summary>
-  public bool WrapText => _styles.CellXfs[Index].WrapText;
+  public bool WrapText {
+    get => _styles.CellXfs[Index].WrapText;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.WrapText, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Readingorder
   /// </summary>
-  public ExcelReadingOrder ReadingOrder => _styles.CellXfs[Index].ReadingOrder;
+  public ExcelReadingOrder ReadingOrder {
+    get => _styles.CellXfs[Index].ReadingOrder;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.ReadingOrder, value, _positionID, _address));
+  }
 
   /// <summary>
   /// Shrink the text to fit
   /// </summary>
-  public bool ShrinkToFit => _styles.CellXfs[Index].ShrinkToFit;
+  public bool ShrinkToFit {
+    get => _styles.CellXfs[Index].ShrinkToFit;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.ShrinkToFit, value, _positionID, _address));
+  }
 
   /// <summary>
   /// The margin between the border and the text
   /// </summary>
-  public int Indent => _styles.CellXfs[Index].Indent;
+  public int Indent {
+    get => _styles.CellXfs[Index].Indent;
+    set {
+      if (value < 0 || value > 250) {
+        throw (new ArgumentOutOfRangeException("Indent must be between 0 and 250"));
+      }
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.Indent, value, _positionID, _address));
+    }
+  }
 
   /// <summary>
   /// Text orientation in degrees. Values range from 0 to 180.
   /// </summary>
-  public int TextRotation => _styles.CellXfs[Index].TextRotation;
+  public int TextRotation {
+    get => _styles.CellXfs[Index].TextRotation;
+    set {
+      if (value < 0 || value > 180) {
+        throw new ArgumentOutOfRangeException("TextRotation out of range.");
+      }
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.TextRotation, value, _positionID, _address));
+    }
+  }
 
   /// <summary>
   /// If true the cell is locked for editing when the sheet is protected
   /// <seealso cref="ExcelWorksheet.Protection"/>
   /// </summary>
-  public bool Locked => _styles.CellXfs[Index].Locked;
+  public bool Locked {
+    get => _styles.CellXfs[Index].Locked;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.Locked, value, _positionID, _address));
+  }
 
   /// <summary>
   /// If true the formula is hidden when the sheet is protected.
   /// <seealso cref="ExcelWorksheet.Protection"/>
   /// </summary>
-  public bool Hidden => _styles.CellXfs[Index].Hidden;
+  public bool Hidden {
+    get => _styles.CellXfs[Index].Hidden;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.Hidden, value, _positionID, _address));
+  }
 
   /// <summary>
   /// The index in the style collection
   /// </summary>
-  public int XfId => _styles.CellXfs[Index].XfId;
+  public int XfId {
+    get => _styles.CellXfs[Index].XfId;
+    set =>
+      _ChangedEvent(
+          this,
+          new(eStyleClass.Style, eStyleProperty.XfId, value, _positionID, _address));
+  }
 
   internal int PositionID { get; set; }
 
diff --git a/EPPlus/Style/ExcelTextFont.cs b/EPPlus/Style/ExcelTextFont.cs
index 9252413..f098072 100644
--- a/EPPlus/Style/ExcelTextFont.cs
+++ b/EPPlus/Style/ExcelTextFont.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using System.Globalization;
 using System.Xml;
 
@@ -140,6 +141,22 @@
     }
   }
 
+  private readonly string _underLineColorPath = "a:uFill/a:solidFill/a:srgbClr/@val";
+
+  public Color UnderLineColor {
+    get {
+      string col = GetXmlNodeString(_underLineColorPath);
+      if (col == "") {
+        return Color.Empty;
+      }
+      return Color.FromArgb(int.Parse(col, NumberStyles.AllowHexSpecifier));
+    }
+    set {
+      CreateTopNode();
+      SetXmlNodeString(_underLineColorPath, value.ToArgb().ToString("X").Substring(2, 6));
+    }
+  }
+
   private readonly string _italicPath = "@i";
 
   public bool Italic {
@@ -170,6 +187,22 @@
     }
   }
 
+  private readonly string _colorPath = "a:solidFill/a:srgbClr/@val";
+
+  public Color Color {
+    get {
+      string col = GetXmlNodeString(_colorPath);
+      if (col == "") {
+        return Color.Empty;
+      }
+      return Color.FromArgb(int.Parse(col, NumberStyles.AllowHexSpecifier));
+    }
+    set {
+      CreateTopNode();
+      SetXmlNodeString(_colorPath, value.ToArgb().ToString("X").Substring(2, 6));
+    }
+  }
+
   private eUnderLineType TranslateUnderline(string text) {
     switch (text) {
       case "sng":
@@ -179,9 +212,7 @@
       case "":
         return eUnderLineType.None;
       default:
-        return Enum.TryParse<eUnderLineType>(text, true, out var result)
-            ? result
-            : eUnderLineType.None;
+        return (eUnderLineType)Enum.Parse(typeof(eUnderLineType), text);
     }
   }
 
@@ -219,4 +250,26 @@
         return "noStrike";
     }
   }
+
+  /// <summary>
+  /// Set the font style from a font object
+  /// </summary>
+  /// <param name="font"></param>
+  public void SetFromFont(Font font) {
+    LatinFont = font.Name;
+    ComplexFont = font.Name;
+    Size = font.Size;
+    if (font.Bold) {
+      Bold = font.Bold;
+    }
+    if (font.Italic) {
+      Italic = font.Italic;
+    }
+    if (font.Underline) {
+      UnderLine = eUnderLineType.Single;
+    }
+    if (font.Strikeout) {
+      Strike = eStrikeType.Single;
+    }
+  }
 }
diff --git a/EPPlus/Style/StyleBase.cs b/EPPlus/Style/StyleBase.cs
index ec560a5..fa1b390 100644
--- a/EPPlus/Style/StyleBase.cs
+++ b/EPPlus/Style/StyleBase.cs
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * You may amend and distribute as you like, but don't remove this header!
  *
  * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
@@ -163,11 +163,17 @@
 
 public abstract class StyleBase {
   protected ExcelStyles _styles;
+  internal XmlHelper.ChangedEventHandler _ChangedEvent;
   protected int _positionID;
   protected string _address;
 
-  internal StyleBase(ExcelStyles styles, int positionId, string address) {
+  internal StyleBase(
+      ExcelStyles styles,
+      XmlHelper.ChangedEventHandler changedEvent,
+      int positionId,
+      string address) {
     _styles = styles;
+    _ChangedEvent = changedEvent;
     _address = address;
     _positionID = positionId;
   }
diff --git a/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs b/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs
index 9c0de5e..7e94481 100644
--- a/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs
@@ -61,9 +61,14 @@
     if (style == "") {
       return ExcelBorderStyle.None;
     }
-    return Enum.TryParse<ExcelBorderStyle>(style, true, out var result)
-        ? result
-        : ExcelBorderStyle.None;
+    string sInStyle =
+        style.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture)
+            + style.Substring(1, style.Length - 1);
+    try {
+      return (ExcelBorderStyle)Enum.Parse(typeof(ExcelBorderStyle), sInStyle);
+    } catch {
+      return ExcelBorderStyle.None;
+    }
   }
 
   private ExcelBorderStyle _borderStyle = ExcelBorderStyle.None;
diff --git a/EPPlus/Style/XmlAccess/ExcelColorXml.cs b/EPPlus/Style/XmlAccess/ExcelColorXml.cs
index 7a6d024..ba1fd11 100644
--- a/EPPlus/Style/XmlAccess/ExcelColorXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelColorXml.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using System.Globalization;
 using System.Xml;
 
@@ -141,6 +142,11 @@
     _auto = false;
   }
 
+  public void SetColor(Color color) {
+    Clear();
+    _rgb = color.ToArgb().ToString("X");
+  }
+
   internal ExcelColorXml Copy() {
     return new(NameSpaceManager) {
       _indexed = _indexed,
diff --git a/EPPlus/Style/XmlAccess/ExcelFillXml.cs b/EPPlus/Style/XmlAccess/ExcelFillXml.cs
index cf43d0e..98b7c9d 100644
--- a/EPPlus/Style/XmlAccess/ExcelFillXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelFillXml.cs
@@ -55,9 +55,17 @@
   }
 
   private ExcelFillStyle GetPatternType(string patternType) {
-    return Enum.TryParse<ExcelFillStyle>(patternType, true, out var result)
-        ? result
-        : ExcelFillStyle.None;
+    if (patternType == "") {
+      return ExcelFillStyle.None;
+    }
+    patternType =
+        patternType.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture)
+            + patternType.Substring(1, patternType.Length - 1);
+    try {
+      return (ExcelFillStyle)Enum.Parse(typeof(ExcelFillStyle), patternType);
+    } catch {
+      return ExcelFillStyle.None;
+    }
   }
 
   internal override string Id => PatternType + PatternColor.Id + BackgroundColor.Id;
diff --git a/EPPlus/Style/XmlAccess/ExcelFontXml.cs b/EPPlus/Style/XmlAccess/ExcelFontXml.cs
index 2d90c95..88ab554 100644
--- a/EPPlus/Style/XmlAccess/ExcelFontXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelFontXml.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using System.Globalization;
 using System.Xml;
 
@@ -70,9 +71,7 @@
       if (ut == "") {
         _underlineType = ExcelUnderLineType.Single;
       } else {
-        if (!Enum.TryParse(ut, true, out _underlineType)) {
-          _underlineType = ExcelUnderLineType.Single;
-        }
+        _underlineType = (ExcelUnderLineType)Enum.Parse(typeof(ExcelUnderLineType), ut, true);
       }
     } else {
       _underlineType = ExcelUnderLineType.None;
@@ -223,6 +222,16 @@
     set => _verticalAlign = value;
   }
 
+  public void SetFromFont(Font font) {
+    Name = font.Name;
+    //Family=fnt.FontFamily.;
+    Size = (int)font.Size;
+    Strike = font.Strikeout;
+    Bold = font.Bold;
+    UnderLine = font.Underline;
+    Italic = font.Italic;
+  }
+
   internal ExcelFontXml Copy() {
     ExcelFontXml newFont = new ExcelFontXml(NameSpaceManager);
     newFont.Name = _name;
diff --git a/EPPlus/Style/XmlAccess/ExcelNamedStyleXml.cs b/EPPlus/Style/XmlAccess/ExcelNamedStyleXml.cs
index 356e90f..8004056 100644
--- a/EPPlus/Style/XmlAccess/ExcelNamedStyleXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelNamedStyleXml.cs
@@ -57,7 +57,7 @@
     CustomBuildin = GetXmlNodeBool(_customBuiltinPath);
 
     _styles = styles;
-    _style = new(styles, -1, Name, _styleXfId);
+    _style = new(styles, styles.NamedStylePropertyChange, -1, Name, _styleXfId);
   }
 
   internal override string Id => Name;
diff --git a/EPPlus/Style/XmlAccess/ExcelXfsXml.cs b/EPPlus/Style/XmlAccess/ExcelXfsXml.cs
index 9761ea1..d00ed0e 100644
--- a/EPPlus/Style/XmlAccess/ExcelXfsXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelXfsXml.cs
@@ -31,6 +31,7 @@
  *******************************************************************************/
 
 using System;
+using System.Drawing;
 using System.Globalization;
 using System.Xml;
 
@@ -81,15 +82,45 @@
     }
   }
 
-  private ExcelHorizontalAlignment GetHorizontalAlign(string align) =>
-    Enum.TryParse<ExcelHorizontalAlignment>(align, true, out var result)
-        ? result
-        : ExcelHorizontalAlignment.General;
+  private ExcelHorizontalAlignment GetHorizontalAlign(string align) {
+    if (align == "") {
+      return ExcelHorizontalAlignment.General;
+    }
+    align =
+        align.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture)
+            + align.Substring(1, align.Length - 1);
+    try {
+      return (ExcelHorizontalAlignment)Enum.Parse(typeof(ExcelHorizontalAlignment), align);
+    } catch {
+      return ExcelHorizontalAlignment.General;
+    }
+  }
 
-  private ExcelVerticalAlignment GetVerticalAlign(string align) =>
-    Enum.TryParse<ExcelVerticalAlignment>(align, true, out var result)
-        ? result
-        : ExcelVerticalAlignment.Bottom;
+  private ExcelVerticalAlignment GetVerticalAlign(string align) {
+    if (align == "") {
+      return ExcelVerticalAlignment.Bottom;
+    }
+    align =
+        align.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture)
+            + align.Substring(1, align.Length - 1);
+    try {
+      return (ExcelVerticalAlignment)Enum.Parse(typeof(ExcelVerticalAlignment), align);
+    } catch {
+      return ExcelVerticalAlignment.Bottom;
+    }
+  }
+
+  internal void Xf_ChangedEvent(object sender, EventArgs e) {
+    //if (_cell != null)
+    //{
+    //    if (!Styles.ChangedCells.ContainsKey(_cell.Id))
+    //    {
+    //        //_cell.Style = "";
+    //        _cell.SetNewStyleID(int.MinValue.ToString());
+    //        Styles.ChangedCells.Add(_cell.Id, _cell);
+    //    }
+    //}
+  }
 
   private int _xfID;
 
@@ -320,6 +351,327 @@
     return newXf;
   }
 
+  internal int GetNewId(
+      ExcelStyleCollection<ExcelXfs> xfsCol,
+      StyleBase styleObject,
+      eStyleClass styleClass,
+      eStyleProperty styleProperty,
+      object value) {
+    ExcelXfs newXfs = Copy();
+    switch (styleClass) {
+      case eStyleClass.Numberformat:
+        newXfs.NumberFormatId = GetIdNumberFormat(styleProperty, value);
+        styleObject.SetIndex(newXfs.NumberFormatId);
+        break;
+      case eStyleClass.Font: {
+        newXfs.FontId = GetIdFont(styleProperty, value);
+        styleObject.SetIndex(newXfs.FontId);
+        break;
+      }
+      case eStyleClass.Fill:
+      case eStyleClass.FillBackgroundColor:
+      case eStyleClass.FillPatternColor:
+        newXfs.FillId = GetIdFill(styleClass, styleProperty, value);
+        styleObject.SetIndex(newXfs.FillId);
+        break;
+      case eStyleClass.GradientFill:
+      case eStyleClass.FillGradientColor1:
+      case eStyleClass.FillGradientColor2:
+        newXfs.FillId = GetIdGradientFill(styleClass, styleProperty, value);
+        styleObject.SetIndex(newXfs.FillId);
+        break;
+      case eStyleClass.Border:
+      case eStyleClass.BorderBottom:
+      case eStyleClass.BorderDiagonal:
+      case eStyleClass.BorderLeft:
+      case eStyleClass.BorderRight:
+      case eStyleClass.BorderTop:
+        newXfs.BorderId = GetIdBorder(styleClass, styleProperty, value);
+        styleObject.SetIndex(newXfs.BorderId);
+        break;
+      case eStyleClass.Style:
+        switch (styleProperty) {
+          case eStyleProperty.XfId:
+            newXfs.XfId = (int)value;
+            break;
+          case eStyleProperty.HorizontalAlign:
+            newXfs.HorizontalAlignment = (ExcelHorizontalAlignment)value;
+            break;
+          case eStyleProperty.VerticalAlign:
+            newXfs.VerticalAlignment = (ExcelVerticalAlignment)value;
+            break;
+          case eStyleProperty.WrapText:
+            newXfs.WrapText = (bool)value;
+            break;
+          case eStyleProperty.ReadingOrder:
+            newXfs.ReadingOrder = (ExcelReadingOrder)value;
+            break;
+          case eStyleProperty.ShrinkToFit:
+            newXfs.ShrinkToFit = (bool)value;
+            break;
+          case eStyleProperty.Indent:
+            newXfs.Indent = (int)value;
+            break;
+          case eStyleProperty.TextRotation:
+            newXfs.TextRotation = (int)value;
+            break;
+          case eStyleProperty.Locked:
+            newXfs.Locked = (bool)value;
+            break;
+          case eStyleProperty.Hidden:
+            newXfs.Hidden = (bool)value;
+            break;
+          default:
+            throw (new("Invalid property for class style."));
+        }
+        break;
+    }
+    int id = xfsCol.FindIndexById(newXfs.Id);
+    if (id < 0) {
+      return xfsCol.Add(newXfs.Id, newXfs);
+    }
+    return id;
+  }
+
+  private int GetIdBorder(eStyleClass styleClass, eStyleProperty styleProperty, object value) {
+    ExcelBorderXml border = Border.Copy();
+
+    switch (styleClass) {
+      case eStyleClass.BorderBottom:
+        SetBorderItem(border.Bottom, styleProperty, value);
+        break;
+      case eStyleClass.BorderDiagonal:
+        SetBorderItem(border.Diagonal, styleProperty, value);
+        break;
+      case eStyleClass.BorderLeft:
+        SetBorderItem(border.Left, styleProperty, value);
+        break;
+      case eStyleClass.BorderRight:
+        SetBorderItem(border.Right, styleProperty, value);
+        break;
+      case eStyleClass.BorderTop:
+        SetBorderItem(border.Top, styleProperty, value);
+        break;
+      case eStyleClass.Border:
+        if (styleProperty == eStyleProperty.BorderDiagonalUp) {
+          border.DiagonalUp = (bool)value;
+        } else if (styleProperty == eStyleProperty.BorderDiagonalDown) {
+          border.DiagonalDown = (bool)value;
+        } else {
+          throw (new("Invalid property for class Border."));
+        }
+        break;
+      default:
+        throw (new("Invalid class/property for class Border."));
+    }
+    int subId;
+    string id = border.Id;
+    subId = _styles.Borders.FindIndexById(id);
+    if (subId == int.MinValue) {
+      return _styles.Borders.Add(id, border);
+    }
+    return subId;
+  }
+
+  private void SetBorderItem(
+      ExcelBorderItemXml excelBorderItem,
+      eStyleProperty styleProperty,
+      object value) {
+    if (styleProperty == eStyleProperty.Style) {
+      excelBorderItem.Style = (ExcelBorderStyle)value;
+    } else if (styleProperty == eStyleProperty.Color
+        || styleProperty == eStyleProperty.Tint
+        || styleProperty == eStyleProperty.IndexedColor) {
+      if (excelBorderItem.Style == ExcelBorderStyle.None) {
+        throw (new("Can't set bordercolor when style is not set."));
+      }
+      excelBorderItem.Color.Rgb = value.ToString();
+    }
+  }
+
+  private int GetIdFill(eStyleClass styleClass, eStyleProperty styleProperty, object value) {
+    ExcelFillXml fill = Fill.Copy();
+
+    switch (styleProperty) {
+      case eStyleProperty.PatternType:
+        if (fill is ExcelGradientFillXml) {
+          fill = new(NameSpaceManager);
+        }
+        fill.PatternType = (ExcelFillStyle)value;
+        break;
+      case eStyleProperty.Color:
+      case eStyleProperty.Tint:
+      case eStyleProperty.IndexedColor:
+      case eStyleProperty.AutoColor:
+        if (fill is ExcelGradientFillXml) {
+          fill = new(NameSpaceManager);
+        }
+        if (fill.PatternType == ExcelFillStyle.None) {
+          throw (new ArgumentException("Can't set color when patterntype is not set."));
+        }
+        ExcelColorXml destColor;
+        if (styleClass == eStyleClass.FillPatternColor) {
+          destColor = fill.PatternColor;
+        } else {
+          destColor = fill.BackgroundColor;
+        }
+
+        if (styleProperty == eStyleProperty.Color) {
+          destColor.Rgb = value.ToString();
+        } else if (styleProperty == eStyleProperty.Tint) {
+          destColor.Tint = (decimal)value;
+        } else if (styleProperty == eStyleProperty.IndexedColor) {
+          destColor.Indexed = (int)value;
+        } else {
+          destColor.Auto = (bool)value;
+        }
+
+        break;
+      default:
+        throw (new ArgumentException("Invalid class/property for class Fill."));
+    }
+    int subId;
+    string id = fill.Id;
+    subId = _styles.Fills.FindIndexById(id);
+    if (subId == int.MinValue) {
+      return _styles.Fills.Add(id, fill);
+    }
+    return subId;
+  }
+
+  private int GetIdGradientFill(
+      eStyleClass styleClass,
+      eStyleProperty styleProperty,
+      object value) {
+    ExcelGradientFillXml fill;
+    if (Fill is ExcelGradientFillXml) {
+      fill = (ExcelGradientFillXml)Fill.Copy();
+    } else {
+      fill = new(Fill.NameSpaceManager);
+      fill.GradientColor1.SetColor(Color.White);
+      fill.GradientColor2.SetColor(Color.FromArgb(79, 129, 189));
+      fill.Type = ExcelFillGradientType.Linear;
+      fill.Degree = 90;
+      fill.Top = double.NaN;
+      fill.Bottom = double.NaN;
+      fill.Left = double.NaN;
+      fill.Right = double.NaN;
+    }
+
+    switch (styleProperty) {
+      case eStyleProperty.GradientType:
+        fill.Type = (ExcelFillGradientType)value;
+        break;
+      case eStyleProperty.GradientDegree:
+        fill.Degree = (double)value;
+        break;
+      case eStyleProperty.GradientTop:
+        fill.Top = (double)value;
+        break;
+      case eStyleProperty.GradientBottom:
+        fill.Bottom = (double)value;
+        break;
+      case eStyleProperty.GradientLeft:
+        fill.Left = (double)value;
+        break;
+      case eStyleProperty.GradientRight:
+        fill.Right = (double)value;
+        break;
+      case eStyleProperty.Color:
+      case eStyleProperty.Tint:
+      case eStyleProperty.IndexedColor:
+      case eStyleProperty.AutoColor:
+        ExcelColorXml destColor;
+
+        if (styleClass == eStyleClass.FillGradientColor1) {
+          destColor = fill.GradientColor1;
+        } else {
+          destColor = fill.GradientColor2;
+        }
+
+        if (styleProperty == eStyleProperty.Color) {
+          destColor.Rgb = value.ToString();
+        } else if (styleProperty == eStyleProperty.Tint) {
+          destColor.Tint = (decimal)value;
+        } else if (styleProperty == eStyleProperty.IndexedColor) {
+          destColor.Indexed = (int)value;
+        } else {
+          destColor.Auto = (bool)value;
+        }
+        break;
+      default:
+        throw (new ArgumentException("Invalid class/property for class Fill."));
+    }
+    int subId;
+    string id = fill.Id;
+    subId = _styles.Fills.FindIndexById(id);
+    if (subId == int.MinValue) {
+      return _styles.Fills.Add(id, fill);
+    }
+    return subId;
+  }
+
+  private int GetIdNumberFormat(eStyleProperty styleProperty, object value) {
+    if (styleProperty == eStyleProperty.Format) {
+      ExcelNumberFormatXml item = null;
+      if (!_styles.NumberFormats.FindById(value.ToString(), ref item)) {
+        item = new(NameSpaceManager) {
+          Format = value.ToString(),
+          NumFmtId = _styles.NumberFormats.NextId++,
+        };
+        _styles.NumberFormats.Add(value.ToString(), item);
+      }
+      return item.NumFmtId;
+    }
+    throw (new("Invalid property for class Numberformat"));
+  }
+
+  private int GetIdFont(eStyleProperty styleProperty, object value) {
+    ExcelFontXml fnt = Font.Copy();
+
+    switch (styleProperty) {
+      case eStyleProperty.Name:
+        fnt.Name = value.ToString();
+        break;
+      case eStyleProperty.Size:
+        fnt.Size = (float)value;
+        break;
+      case eStyleProperty.Family:
+        fnt.Family = (int)value;
+        break;
+      case eStyleProperty.Bold:
+        fnt.Bold = (bool)value;
+        break;
+      case eStyleProperty.Italic:
+        fnt.Italic = (bool)value;
+        break;
+      case eStyleProperty.Strike:
+        fnt.Strike = (bool)value;
+        break;
+      case eStyleProperty.UnderlineType:
+        fnt.UnderLineType = (ExcelUnderLineType)value;
+        break;
+      case eStyleProperty.Color:
+        fnt.Color.Rgb = value.ToString();
+        break;
+      case eStyleProperty.VerticalAlign:
+        fnt.VerticalAlign =
+            ((ExcelVerticalAlignmentFont)value) == ExcelVerticalAlignmentFont.None
+                ? ""
+                : value.ToString().ToLower(CultureInfo.InvariantCulture);
+        break;
+      default:
+        throw (new("Invalid property for class Font"));
+    }
+    int subId;
+    string id = fnt.Id;
+    subId = _styles.Fonts.FindIndexById(id);
+    if (subId == int.MinValue) {
+      return _styles.Fonts.Add(id, fnt);
+    }
+    return subId;
+  }
+
   internal override XmlNode CreateXmlNode(XmlNode topNode) {
     return CreateXmlNode(topNode, false);
   }
diff --git a/NetCoreTests/ExcelPackageTest.cs b/NetCoreTests/ExcelPackageTest.cs
index 369e55a..8785d5d 100644
--- a/NetCoreTests/ExcelPackageTest.cs
+++ b/NetCoreTests/ExcelPackageTest.cs
@@ -3,7 +3,6 @@
 using FluentAssertions;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using OfficeOpenXml;
-using OfficeOpenXml.DataValidation;
 
 namespace NetCoreTests;
 
@@ -33,20 +32,6 @@
     worksheet.Name.Should().Be("pusum11a");
   }
 
-  [TestMethod]
-  public void TestGetAsByteArray_DataValidation() {
-    var package = new ExcelPackage(new FileInfo(GetTestWorkbookPath("DataValidation.xlsx")));
-    var data = package.GetAsByteArray();
-
-    // Verify that we can reload
-    var newPackage = new ExcelPackage(new MemoryStream(data, false));
-    newPackage.Workbook.Worksheets.Count.Should().Be(1);
-    var worksheet = newPackage.Workbook.Worksheets.First();
-    worksheet.Name.Should().Be("Sheet1");
-    worksheet.DataValidations.Should().HaveCount(31);
-    worksheet.DataValidations[1].ValidationType.Should().Be(ExcelDataValidationType.Whole);
-  }
-
   private static string GetTestWorkbookPath(string filename) {
     var assemblyPath = Path.GetDirectoryName(typeof(ExcelPackageTest).Assembly.Location)!;
     return Path.Combine(assemblyPath, "TestWorkbooks", filename);
diff --git a/NetCoreTests/TestWorkbooks/DataValidation.xlsx b/NetCoreTests/TestWorkbooks/DataValidation.xlsx
deleted file mode 100644
index 78d508c..0000000
--- a/NetCoreTests/TestWorkbooks/DataValidation.xlsx
+++ /dev/null
Binary files differ