| /******************************************************************************* | 
 |  * 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		        2010-01-28 | 
 |  * Jan Källman		    License changed GPL-->LGPL  2011-12-27 | 
 |  * Eyal Seagull		    Conditional Formatting      2012-04-03 | 
 |  *******************************************************************************/ | 
 |  | 
 | using System; | 
 | using System.Collections; | 
 | using System.Collections.Generic; | 
 | using System.Globalization; | 
 | using System.Security; | 
 | using System.Text; | 
 | using System.Xml; | 
 |  | 
 | namespace AppsheetEpplus; | 
 |  | 
 | /// <summary> | 
 | /// A range of cells | 
 | /// </summary> | 
 | public class ExcelRangeBase | 
 |     : ExcelAddress, | 
 |       IExcelCell, | 
 |       IDisposable, | 
 |       IEnumerable<ExcelRangeBase>, | 
 |       IEnumerator<ExcelRangeBase> { | 
 |   /// <summary> | 
 |   /// Reference to the worksheet | 
 |   /// </summary> | 
 |   protected ExcelWorksheet _worksheet; | 
 |   internal ExcelWorkbook _workbook; | 
 |  | 
 |   private delegate void ChangePropHandler(SetValueHandler method, object value); | 
 |  | 
 |   private delegate void SetValueHandler(object value, int row, int col); | 
 |  | 
 |   private ChangePropHandler _changePropMethod; | 
 |   private int _styleID; | 
 |  | 
 |   private class CopiedCell { | 
 |     internal int Row { get; set; } | 
 |  | 
 |     internal int Column { get; set; } | 
 |  | 
 |     internal object Value { get; set; } | 
 |  | 
 |     internal string Type { get; set; } | 
 |  | 
 |     internal object Formula { get; set; } | 
 |  | 
 |     internal int? StyleID { get; set; } | 
 |  | 
 |     internal Uri HyperLink { get; set; } | 
 |  | 
 |     internal ExcelComment Comment { get; set; } | 
 |  | 
 |     internal Byte Flag { get; set; } | 
 |   } | 
 |  | 
 |   //private class CopiedFlag | 
 |   //{ | 
 |   //    internal int Row { get; set; } | 
 |   //    internal int Column { get; set; } | 
 |   //    internal Byte Flag { get; set; } | 
 |   //} | 
 |  | 
 |  | 
 |   internal ExcelRangeBase(ExcelWorksheet worksheet) { | 
 |     _worksheet = worksheet; | 
 |     _ws = _worksheet.Name; | 
 |     _workbook = _worksheet.Workbook; | 
 |     AddressChange += ExcelRangeBase_AddressChange; | 
 |     SetDelegate(); | 
 |   } | 
 |  | 
 |   private void ExcelRangeBase_AddressChange(object sender, EventArgs e) { | 
 |     if (Table != null) { | 
 |       SetRcFromTable(_workbook, null); | 
 |     } | 
 |     SetDelegate(); | 
 |   } | 
 |  | 
 |   internal ExcelRangeBase(ExcelWorksheet worksheet, string address) | 
 |       : base(worksheet == null ? "" : worksheet.Name, address) { | 
 |     _worksheet = worksheet; | 
 |     _workbook = worksheet.Workbook; | 
 |     SetRcFromTable(_workbook, null); | 
 |     if (string.IsNullOrEmpty(_ws)) { | 
 |       _ws = _worksheet == null ? "" : _worksheet.Name; | 
 |     } | 
 |     AddressChange += ExcelRangeBase_AddressChange; | 
 |     SetDelegate(); | 
 |   } | 
 |  | 
 |   internal ExcelRangeBase(ExcelWorkbook wb, ExcelWorksheet xlWorksheet, string address, bool isName) | 
 |       : base(xlWorksheet == null ? "" : xlWorksheet.Name, address, isName) { | 
 |     SetRcFromTable(wb, null); | 
 |     _worksheet = xlWorksheet; | 
 |     _workbook = wb; | 
 |     if (string.IsNullOrEmpty(_ws)) { | 
 |       _ws = xlWorksheet?.Name; | 
 |     } | 
 |     AddressChange += ExcelRangeBase_AddressChange; | 
 |     SetDelegate(); | 
 |   } | 
 |  | 
 |   ~ExcelRangeBase() { | 
 |     //this.AddressChange -= new EventHandler(ExcelRangeBase_AddressChange); | 
 |   } | 
 |  | 
 |   private void SetDelegate() { | 
 |     if (_fromRow == -1) { | 
 |       _changePropMethod = SetUnknown; | 
 |     } | 
 |     //Single cell | 
 |     else if (_fromRow == _toRow && _fromCol == _toCol && Addresses == null) { | 
 |       _changePropMethod = SetSingle; | 
 |     } | 
 |     //Range (ex A1:A2) | 
 |     else if (Addresses == null) { | 
 |       _changePropMethod = SetRange; | 
 |     } | 
 |     //Multi Range (ex A1:A2,C1:C2) | 
 |     else { | 
 |       _changePropMethod = SetMultiRange; | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// We dont know the address yet. Set the delegate first time a property is set. | 
 |   /// </summary> | 
 |   /// <param name="valueMethod"></param> | 
 |   /// <param name="value"></param> | 
 |   private void SetUnknown(SetValueHandler valueMethod, object value) { | 
 |     //Address is not set use, selected range | 
 |     if (_fromRow == -1) { | 
 |       SetToSelectedRange(); | 
 |     } | 
 |     SetDelegate(); | 
 |     _changePropMethod(valueMethod, value); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Set a single cell | 
 |   /// </summary> | 
 |   /// <param name="valueMethod"></param> | 
 |   /// <param name="value"></param> | 
 |   private void SetSingle(SetValueHandler valueMethod, object value) { | 
 |     valueMethod(value, _fromRow, _fromCol); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Set a range | 
 |   /// </summary> | 
 |   /// <param name="valueMethod"></param> | 
 |   /// <param name="value"></param> | 
 |   private void SetRange(SetValueHandler valueMethod, object value) { | 
 |     SetValueAddress(this, valueMethod, value); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Set a multirange (A1:A2,C1:C2) | 
 |   /// </summary> | 
 |   /// <param name="valueMethod"></param> | 
 |   /// <param name="value"></param> | 
 |   private void SetMultiRange(SetValueHandler valueMethod, object value) { | 
 |     SetValueAddress(this, valueMethod, value); | 
 |     foreach (var address in Addresses) { | 
 |       SetValueAddress(address, valueMethod, value); | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Set the property for an address | 
 |   /// </summary> | 
 |   /// <param name="address"></param> | 
 |   /// <param name="valueMethod"></param> | 
 |   /// <param name="value"></param> | 
 |   private void SetValueAddress(ExcelAddress address, SetValueHandler valueMethod, object value) { | 
 |     IsRangeValid(""); | 
 |     if (_fromRow == 1 | 
 |         && _fromCol == 1 | 
 |         && _toRow == ExcelPackage.MaxRows | 
 |         && _toCol | 
 |             == ExcelPackage.MaxColumns) //Full sheet (ex ws.Cells.Value=0). Set value for A1 only to avoid hanging | 
 |     { | 
 |       throw (new ArgumentException( | 
 |               "Can't reference all cells. Please use the indexer to set the range")); | 
 |     } | 
 |     for (int col = address.Start.Column; col <= address.End.Column; col++) { | 
 |       for (int row = address.Start.Row; row <= address.End.Row; row++) { | 
 |         valueMethod(value, row, col); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private void Set_StyleID(object value, int row, int col) { | 
 |     _worksheet._styles.SetValue(row, col, (int)value); | 
 |   } | 
 |  | 
 |   private void Set_Value(object value, int row, int col) { | 
 |     //ExcelCell c = _worksheet.Cell(row, col); | 
 |     var sfi = _worksheet._formulas.GetValue(row, col); | 
 |     if (sfi is int) { | 
 |       SplitFormulas(_worksheet.Cells[row, col]); | 
 |     } | 
 |     if (sfi != null) { | 
 |       _worksheet._formulas.SetValue(row, col, string.Empty); | 
 |     } | 
 |     _worksheet._values.SetValue(row, col, value); | 
 |   } | 
 |  | 
 |   private void Set_Formula(object value, int row, int col) { | 
 |     //ExcelCell c = _worksheet.Cell(row, col); | 
 |     var f = _worksheet._formulas.GetValue(row, col); | 
 |     if (f is int i && i >= 0) { | 
 |       SplitFormulas(_worksheet.Cells[row, col]); | 
 |     } | 
 |  | 
 |     string formula = (value == null ? string.Empty : value.ToString()); | 
 |     if (formula == string.Empty) { | 
 |       _worksheet._formulas.SetValue(row, col, string.Empty); | 
 |     } else { | 
 |       if (formula[0] == '=') { | 
 |         value = formula.Substring(1, formula.Length - 1); // remove any starting equalsign. | 
 |       } | 
 |       _worksheet._formulas.SetValue(row, col, formula); | 
 |       _worksheet._values.SetValue(row, col, null); | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Handles shared formulas | 
 |   /// </summary> | 
 |   /// <param name="value">The  formula</param> | 
 |   /// <param name="address">The address of the formula</param> | 
 |   /// <param name="isArray">If the forumla is an array formula.</param> | 
 |   private void Set_SharedFormula(string value, ExcelAddress address, bool isArray) { | 
 |     if (_fromRow == 1 | 
 |         && _fromCol == 1 | 
 |         && _toRow == ExcelPackage.MaxRows | 
 |         && _toCol | 
 |             == ExcelPackage.MaxColumns) //Full sheet (ex ws.Cells.Value=0). Set value for A1 only to avoid hanging | 
 |     { | 
 |       throw (new InvalidOperationException("Can't set a formula for the entire worksheet")); | 
 |     } | 
 |     if (address.Start.Row == address.End.Row | 
 |         && address.Start.Column == address.End.Column | 
 |         && !isArray) //is it really a shared formula? Arrayformulas can be one cell only | 
 |     { | 
 |       //Nope, single cell. Set the formula | 
 |       Set_Formula(value, address.Start.Row, address.Start.Column); | 
 |       return; | 
 |     } | 
 |     //RemoveFormuls(address); | 
 |     CheckAndSplitSharedFormula(address); | 
 |     ExcelWorksheet.Formulas f = new ExcelWorksheet.Formulas(SourceCodeTokenizer.Default); | 
 |     f.Formula = value; | 
 |     f.Index = _worksheet.GetMaxShareFunctionIndex(isArray); | 
 |     f.Address = address.FirstAddress; | 
 |     f.StartCol = address.Start.Column; | 
 |     f.StartRow = address.Start.Row; | 
 |     f.IsArray = isArray; | 
 |  | 
 |     _worksheet._sharedFormulas.Add(f.Index, f); | 
 |     //_worksheet.Cell(address.Start.Row, address.Start.Column).SharedFormulaID = f.Index; | 
 |     //_worksheet.Cell(address.Start.Row, address.Start.Column).Formula = value; | 
 |  | 
 |     for (int col = address.Start.Column; col <= address.End.Column; col++) { | 
 |       for (int row = address.Start.Row; row <= address.End.Row; row++) { | 
 |         //_worksheet.Cell(row, col).SharedFormulaID = f.Index; | 
 |         _worksheet._formulas.SetValue(row, col, f.Index); | 
 |         _worksheet._values.SetValue(row, col, null); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private void Set_HyperLink(object value, int row, int col) { | 
 |     //_worksheet.Cell(row, col).Hyperlink = value as Uri; | 
 |     if (value is Uri uri) { | 
 |       _worksheet._hyperLinks.SetValue(row, col, uri); | 
 |  | 
 |       if (uri is ExcelHyperLink link) { | 
 |         _worksheet._values.SetValue(row, col, link.Display); | 
 |       } else { | 
 |         _worksheet._values.SetValue(row, col, uri.OriginalString); | 
 |       } | 
 |     } else { | 
 |       _worksheet._hyperLinks.SetValue(row, col, null); | 
 |       _worksheet._values.SetValue(row, col, null); | 
 |     } | 
 |   } | 
 |  | 
 |   private void Set_IsArrayFormula(object value, int row, int col) { | 
 |     _worksheet._flags.SetFlagValue(row, col, (bool)value, CellFlags.ArrayFormula); | 
 |   } | 
 |  | 
 |   private void Set_IsRichText(object value, int row, int col) { | 
 |     //_worksheet.Cell(row, col).IsRichText = (bool)value; | 
 |     _worksheet._flags.SetFlagValue(row, col, (bool)value, CellFlags.RichText); | 
 |   } | 
 |  | 
 |   private void Exists_Comment(object value, int row, int col) { | 
 |     ulong cellId = GetCellId(_worksheet.SheetID, row, col); | 
 |     if (_worksheet.Comments._comments.ContainsKey(cellId)) { | 
 |       throw (new InvalidOperationException( | 
 |               string.Format( | 
 |                   "Cell {0} already contain a comment.", | 
 |                   new ExcelCellAddress(row, col).Address))); | 
 |     } | 
 |   } | 
 |  | 
 |   private void SetToSelectedRange() { | 
 |     if (_worksheet.View.SelectedRange == "") { | 
 |       Address = "A1"; | 
 |     } else { | 
 |       Address = _worksheet.View.SelectedRange; | 
 |     } | 
 |   } | 
 |  | 
 |   private void IsRangeValid(string type) { | 
 |     if (_fromRow <= 0) { | 
 |       if (_address == "") { | 
 |         SetToSelectedRange(); | 
 |       } else { | 
 |         if (type == "") { | 
 |           throw (new InvalidOperationException( | 
 |                   string.Format("Range is not valid for this operation: {0}", _address))); | 
 |         } | 
 |         throw (new InvalidOperationException( | 
 |                 string.Format("Range is not valid for {0} : {1}", type, _address))); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// The styleobject for the range. | 
 |   /// </summary> | 
 |   public ExcelStyle Style { | 
 |     get { | 
 |       IsRangeValid("styling"); | 
 |       int s = 0; | 
 |       if (!_worksheet._styles.Exists( | 
 |           _fromRow, | 
 |           _fromCol, | 
 |           ref s)) //Cell exists | 
 |       { | 
 |         if (!_worksheet._styles.Exists( | 
 |             _fromRow, | 
 |             0, | 
 |             ref s)) //No, check Row style | 
 |         { | 
 |           var c = Worksheet.GetColumn(_fromCol); | 
 |           if (c == null) { | 
 |             s = 0; | 
 |           } else { | 
 |             s = c.StyleID; | 
 |           } | 
 |         } | 
 |       } | 
 |       return _worksheet.Workbook.Styles.GetStyleObject(s, _worksheet.PositionID, Address); | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// The named style | 
 |   /// </summary> | 
 |   public string StyleName { | 
 |     get { | 
 |       IsRangeValid("styling"); | 
 |       int xfId; | 
 |       if (_fromRow == 1 && _toRow == ExcelPackage.MaxRows) { | 
 |         xfId = GetColumnStyle(_fromCol); | 
 |       } else if (_fromCol == 1 && _toCol == ExcelPackage.MaxColumns) { | 
 |         xfId = 0; | 
 |         if (!_worksheet._styles.Exists(_fromRow, 0, ref xfId)) { | 
 |           xfId = GetColumnStyle(_fromCol); | 
 |         } | 
 |       } else { | 
 |         xfId = 0; | 
 |         if (!_worksheet._styles.Exists(_fromRow, _fromCol, ref xfId)) { | 
 |           if (!_worksheet._styles.Exists(_fromRow, 0, ref xfId)) { | 
 |             xfId = GetColumnStyle(_fromCol); | 
 |           } | 
 |         } | 
 |       } | 
 |       int nsId; | 
 |       if (xfId <= 0) { | 
 |         nsId = Style.Styles.CellXfs[0].XfId; | 
 |       } else { | 
 |         nsId = Style.Styles.CellXfs[xfId].XfId; | 
 |       } | 
 |       foreach (var ns in Style.Styles.NamedStyles) { | 
 |         if (ns.StyleXfId == nsId) { | 
 |           return ns.Name; | 
 |         } | 
 |       } | 
 |  | 
 |       return ""; | 
 |     } | 
 |     set { | 
 |       _styleID = _worksheet.Workbook.Styles.GetStyleIdFromName(value); | 
 |       int col = _fromCol; | 
 |       if (_fromRow == 1 | 
 |           && _toRow | 
 |               == ExcelPackage.MaxRows) //Full column | 
 |       { | 
 |         ExcelColumn column; | 
 |         //Get the startcolumn | 
 |         //ulong colID = ExcelColumn.GetColumnID(_worksheet.SheetID, column); | 
 |         var c = _worksheet.GetValue(0, _fromCol); | 
 |         if (c == null) { | 
 |           column = _worksheet.Column(_fromCol); | 
 |           //if (_worksheet._values.PrevCell(ref row, ref col)) | 
 |           //{ | 
 |           //    var prevCol = (ExcelColumn)_worksheet._values.GetValue(row, col); | 
 |           //    column = prevCol.Clone(_worksheet, column); | 
 |           //    prevCol.ColumnMax = column - 1; | 
 |           //} | 
 |         } else { | 
 |           column = (ExcelColumn)c; | 
 |         } | 
 |  | 
 |         column.StyleName = value; | 
 |         column.StyleID = _styleID; | 
 |  | 
 |         //var index = _worksheet._columns.IndexOf(colID); | 
 |         var cols = new CellsStoreEnumerator<object>(_worksheet._values, 0, _fromCol + 1, 0, _toCol); | 
 |         if (cols.Next()) { | 
 |           col = _fromCol; | 
 |           while (column.ColumnMin <= _toCol) { | 
 |             if (column.ColumnMax > _toCol) { | 
 |               var newCol = _worksheet.CopyColumn(column, _toCol + 1, column.ColumnMax); | 
 |               column.ColumnMax = _toCol; | 
 |             } | 
 |  | 
 |             column._styleName = value; | 
 |             column.StyleID = _styleID; | 
 |  | 
 |             if (cols.Value == null) { | 
 |               break; | 
 |             } | 
 |             var nextCol = (ExcelColumn)cols.Value; | 
 |             if (column.ColumnMax < nextCol.ColumnMax - 1) { | 
 |               column.ColumnMax = nextCol.ColumnMax - 1; | 
 |             } | 
 |             column = nextCol; | 
 |             cols.Next(); | 
 |           } | 
 |         } | 
 |         if (column.ColumnMax < _toCol) { | 
 |           column.ColumnMax = _toCol; | 
 |         } | 
 |         //if (column.ColumnMin == column) | 
 |         //{ | 
 |         //    column.ColumnMax = _toCol; | 
 |         //} | 
 |         //else if (column._columnMax < _toCol) | 
 |         //{ | 
 |         //    var newCol = _worksheet.Column(column._columnMax + 1) as ExcelColumn; | 
 |         //    newCol._columnMax = _toCol; | 
 |  | 
 |         //    newCol._styleID = _styleID; | 
 |         //    newCol._styleName = value; | 
 |         //} | 
 |         if (_fromCol == 1 | 
 |             && _toCol | 
 |                 == ExcelPackage.MaxColumns) //FullRow | 
 |         { | 
 |           var rows = new CellsStoreEnumerator<object>( | 
 |               _worksheet._values, | 
 |               1, | 
 |               0, | 
 |               ExcelPackage.MaxRows, | 
 |               0); | 
 |           rows.Next(); | 
 |           while (rows.Value != null) { | 
 |             _worksheet._styles.SetValue(rows.Row, 0, _styleID); | 
 |             if (!rows.Next()) { | 
 |               break; | 
 |             } | 
 |           } | 
 |         } | 
 |       } else if (_fromCol == 1 | 
 |           && _toCol | 
 |               == ExcelPackage.MaxColumns) //FullRow | 
 |       { | 
 |         for (int r = _fromRow; r <= _toRow; r++) { | 
 |           _worksheet.Row(r)._styleName = value; | 
 |           _worksheet.Row(r).StyleID = _styleID; | 
 |         } | 
 |       } | 
 |  | 
 |       if (!((_fromRow == 1 && _toRow == ExcelPackage.MaxRows) | 
 |                   || (_fromCol == 1 | 
 |                           && _toCol | 
 |                               == ExcelPackage.MaxColumns))) //Cell specific | 
 |       { | 
 |         for (int c = _fromCol; c <= _toCol; c++) { | 
 |           for (int r = _fromRow; r <= _toRow; r++) { | 
 |             _worksheet._styles.SetValue(r, c, _styleID); | 
 |           } | 
 |         } | 
 |       } else //Only set name on created cells. (uncreated cells is set on full row or full column). | 
 |       { | 
 |         var cells = new CellsStoreEnumerator<object>( | 
 |             _worksheet._values, | 
 |             _fromRow, | 
 |             _fromCol, | 
 |             _toRow, | 
 |             _toCol); | 
 |         while (cells.Next()) { | 
 |           _worksheet._styles.SetValue(cells.Row, cells.Column, _styleID); | 
 |         } | 
 |       } | 
 |       //_changePropMethod(Set_StyleName, value); | 
 |     } | 
 |   } | 
 |  | 
 |   private int GetColumnStyle(int col) { | 
 |     object c = null; | 
 |     if (_worksheet._values.Exists(0, col, ref c)) { | 
 |       return (c as ExcelColumn).StyleID; | 
 |     } | 
 |     int row = 0; | 
 |     if (_worksheet._values.PrevCell(ref row, ref col)) { | 
 |       var column = _worksheet._values.GetValue(row, col) as ExcelColumn; | 
 |       if (column.ColumnMax >= col) { | 
 |         return _worksheet._styles.GetValue(row, col); | 
 |       } | 
 |     } | 
 |     return 0; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// The style ID. | 
 |   /// It is not recomended to use this one. Use Named styles as an alternative. | 
 |   /// If you do, make sure that you use the Style.UpdateXml() method to update any new styles added to the workbook. | 
 |   /// </summary> | 
 |   public int StyleID { | 
 |     get { | 
 |       int s = 0; | 
 |       if (!_worksheet._styles.Exists(_fromRow, _fromCol, ref s)) { | 
 |         if (!_worksheet._styles.Exists(_fromRow, 0, ref s)) { | 
 |           s = _worksheet._styles.GetValue(0, _fromCol); | 
 |         } | 
 |       } | 
 |       return s; | 
 |     } | 
 |     set => _changePropMethod(Set_StyleID, value); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Set the range to a specific value | 
 |   /// </summary> | 
 |   public object Value { | 
 |     get { | 
 |       if (IsName) { | 
 |         if (_worksheet == null) { | 
 |           return _workbook._names[_address].NameValue; | 
 |         } | 
 |         return _worksheet.Names[_address].NameValue; | 
 |       } | 
 |       if (_fromRow == _toRow && _fromCol == _toCol) { | 
 |         return _worksheet.GetValue(_fromRow, _fromCol); | 
 |       } | 
 |       return GetValueArray(); | 
 |     } | 
 |     set { | 
 |       if (IsName) { | 
 |         if (_worksheet == null) { | 
 |           _workbook._names[_address].NameValue = value; | 
 |         } else { | 
 |           _worksheet.Names[_address].NameValue = value; | 
 |         } | 
 |       } else { | 
 |         _changePropMethod(Set_Value, value); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private object GetValueArray() { | 
 |     ExcelAddressBase addr; | 
 |     if (_fromRow == 1 | 
 |         && _fromCol == 1 | 
 |         && _toRow == ExcelPackage.MaxRows | 
 |         && _toCol == ExcelPackage.MaxColumns) { | 
 |       addr = _worksheet.Dimension; | 
 |       if (addr == null) { | 
 |         return null; | 
 |       } | 
 |     } else { | 
 |       addr = this; | 
 |     } | 
 |     object[,] v = new object[addr._toRow - addr._fromRow + 1, addr._toCol - addr._fromCol + 1]; | 
 |  | 
 |     for (int col = addr._fromCol; col <= addr._toCol; col++) { | 
 |       for (int row = addr._fromRow; row <= addr._toRow; row++) { | 
 |         if (_worksheet._values.Exists(row, col)) { | 
 |           if (_worksheet._flags.GetFlagValue(row, col, CellFlags.RichText)) { | 
 |             v[row - addr._fromRow, col - addr._fromCol] = GetRichText(row, col).Text; | 
 |           } else { | 
 |             v[row - addr._fromRow, col - addr._fromCol] = _worksheet._values.GetValue(row, col); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |     return v; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Returns the formatted value. | 
 |   /// </summary> | 
 |   public string Text => GetFormattedText(false); | 
 |  | 
 |   private string GetFormattedText(bool forWidthCalc) { | 
 |     object v = Value; | 
 |     if (v == null) { | 
 |       return ""; | 
 |     } | 
 |     var styles = Worksheet.Workbook.Styles; | 
 |     var nfId = styles.CellXfs[StyleID].NumberFormatId; | 
 |     ExcelNumberFormatXml.ExcelFormatTranslator nf = null; | 
 |     for (int i = 0; i < styles.NumberFormats.Count; i++) { | 
 |       if (nfId == styles.NumberFormats[i].NumFmtId) { | 
 |         nf = styles.NumberFormats[i].FormatTranslator; | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     string format, | 
 |         textFormat; | 
 |     if (forWidthCalc) { | 
 |       format = nf.NetFormatForWidth; | 
 |       textFormat = nf.NetTextFormatForWidth; | 
 |     } else { | 
 |       format = nf.NetFormat; | 
 |       textFormat = nf.NetTextFormat; | 
 |     } | 
 |  | 
 |     return FormatValue(v, nf, format, textFormat); | 
 |   } | 
 |  | 
 |   internal static string FormatValue( | 
 |       object v, | 
 |       ExcelNumberFormatXml.ExcelFormatTranslator nf, | 
 |       string format, | 
 |       string textFormat) { | 
 |     if (v is decimal || v.GetType().IsPrimitive) { | 
 |       double d; | 
 |       try { | 
 |         d = Convert.ToDouble(v); | 
 |       } catch { | 
 |         return ""; | 
 |       } | 
 |  | 
 |       if (nf.DataType == ExcelNumberFormatXml.eFormatType.Number) { | 
 |         if (string.IsNullOrEmpty(nf.FractionFormat)) { | 
 |           return d.ToString(format, nf.Culture); | 
 |         } | 
 |         return nf.FormatFraction(d); | 
 |       } | 
 |       if (nf.DataType == ExcelNumberFormatXml.eFormatType.DateTime) { | 
 |         var date = DateTime.FromOADate(d); | 
 |         return date.ToString(format, nf.Culture); | 
 |       } | 
 |     } else if (v is DateTime time) { | 
 |       if (nf.DataType == ExcelNumberFormatXml.eFormatType.DateTime) { | 
 |         return time.ToString(format, nf.Culture); | 
 |       } | 
 |       double d = time.ToOADate(); | 
 |       if (string.IsNullOrEmpty(nf.FractionFormat)) { | 
 |         return d.ToString(format, nf.Culture); | 
 |       } | 
 |       return nf.FormatFraction(d); | 
 |     } else if (v is TimeSpan span) { | 
 |       if (nf.DataType == ExcelNumberFormatXml.eFormatType.DateTime) { | 
 |         return new DateTime(span.Ticks).ToString(format, nf.Culture); | 
 |       } | 
 |       double d = (new DateTime(span.Ticks)).ToOADate(); | 
 |       if (string.IsNullOrEmpty(nf.FractionFormat)) { | 
 |         return d.ToString(format, nf.Culture); | 
 |       } | 
 |       return nf.FormatFraction(d); | 
 |     } else { | 
 |       if (textFormat == "") { | 
 |         return v.ToString(); | 
 |       } | 
 |       return string.Format(textFormat, v); | 
 |     } | 
 |     return v.ToString(); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Gets or sets a formula for a range. | 
 |   /// </summary> | 
 |   public string Formula { | 
 |     get { | 
 |       if (IsName) { | 
 |         if (_worksheet == null) { | 
 |           return _workbook._names[_address].NameFormula; | 
 |         } | 
 |         return _worksheet.Names[_address].NameFormula; | 
 |       } | 
 |       return _worksheet.GetFormula(_fromRow, _fromCol); | 
 |     } | 
 |     set { | 
 |       if (IsName) { | 
 |         if (_worksheet == null) { | 
 |           _workbook._names[_address].NameFormula = value; | 
 |         } else { | 
 |           _worksheet.Names[_address].NameFormula = value; | 
 |         } | 
 |       } else { | 
 |         if (value == null || value.Trim() == "") { | 
 |           //Set the cells to null | 
 |           Value = null; | 
 |         } else if (_fromRow == _toRow && _fromCol == _toCol) { | 
 |           Set_Formula(value, _fromRow, _fromCol); | 
 |         } else { | 
 |           Set_SharedFormula(value, this, false); | 
 |           if (Addresses != null) { | 
 |             foreach (var address in Addresses) { | 
 |               Set_SharedFormula(value, address, false); | 
 |             } | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Gets or Set a formula in R1C1 format. | 
 |   /// </summary> | 
 |   public string FormulaR1C1 { | 
 |     get { | 
 |       IsRangeValid("FormulaR1C1"); | 
 |       return _worksheet.GetFormulaR1C1(_fromRow, _fromCol); | 
 |     } | 
 |     set { | 
 |       IsRangeValid("FormulaR1C1"); | 
 |       if (value.Length > 0 && value[0] == '=') { | 
 |         value = value.Substring(1, value.Length - 1); // remove any starting equalsign. | 
 |       } | 
 |  | 
 |       if (value == null || value.Trim() == "") { | 
 |         //Set the cells to null | 
 |         _worksheet.Cells[TranslateFromR1C1(value, _fromRow, _fromCol)].Value = null; | 
 |       } else if (Addresses == null) { | 
 |         Set_SharedFormula(TranslateFromR1C1(value, _fromRow, _fromCol), this, false); | 
 |       } else { | 
 |         Set_SharedFormula( | 
 |             TranslateFromR1C1(value, _fromRow, _fromCol), | 
 |             new(WorkSheet, FirstAddress), | 
 |             false); | 
 |         foreach (var address in Addresses) { | 
 |           Set_SharedFormula( | 
 |               TranslateFromR1C1(value, address.Start.Row, address.Start.Column), | 
 |               address, | 
 |               false); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Gets or Set a formula in R1C1 format. | 
 |   /// | 
 |   public string FormulaR1C1_V1 { | 
 |     get { | 
 |       IsRangeValid("FormulaR1C1"); | 
 |       return _worksheet.GetFormulaR1C1_V1(_fromRow, _fromCol); | 
 |     } | 
 |   } | 
 |  | 
 |   public string ArrayFormulaAddress { | 
 |     get { | 
 |       IsRangeValid("FormulaR1C1"); | 
 |       return _worksheet.GetArrayFormulaAddress(_fromRow, _fromCol); | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Set the hyperlink property for a range of cells | 
 |   /// </summary> | 
 |   public Uri Hyperlink { | 
 |     get { | 
 |       IsRangeValid("formulaR1C1"); | 
 |       return _worksheet._hyperLinks.GetValue(_fromRow, _fromCol); | 
 |     } | 
 |     set => _changePropMethod(Set_HyperLink, value); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// If the cells in the range are merged. | 
 |   /// </summary> | 
 |   public bool Merge { | 
 |     get { | 
 |       IsRangeValid("merging"); | 
 |       for (int col = _fromCol; col <= _toCol; col++) { | 
 |         for (int row = _fromRow; row <= _toRow; row++) { | 
 |           if (_worksheet.MergedCells[row, col] == null) { | 
 |             return false; | 
 |           } | 
 |           //if (!_worksheet._flags.GetFlagValue(row, col, CellFlags.Merged)) | 
 |           //{ | 
 |           //    return false; | 
 |           //} | 
 |         } | 
 |       } | 
 |       return true; | 
 |     } | 
 |     set { | 
 |       IsRangeValid("merging"); | 
 |       //SetMerge(value, FirstAddress); | 
 |       if (value) { | 
 |         _worksheet.MergedCells.Add(new(FirstAddress), true); | 
 |         if (Addresses != null) { | 
 |           foreach (var address in Addresses) { | 
 |             _worksheet.MergedCells.Add(address, true); | 
 |             //SetMerge(value, address._address); | 
 |           } | 
 |         } | 
 |       } else { | 
 |         _worksheet.MergedCells.Clear(this); | 
 |         if (Addresses != null) { | 
 |           foreach (var address in Addresses) { | 
 |             _worksheet.MergedCells.Clear(address); | 
 |             ; | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   //private void SetMerge(bool value, string address) | 
 |   //{ | 
 |   //    if (!value) | 
 |   //    { | 
 |   //        if (_worksheet.MergedCells.List.Contains(address)) | 
 |   //        { | 
 |   //            SetCellMerge(false, address); | 
 |   //            _worksheet.MergedCells.List.Remove(address); | 
 |   //        } | 
 |   //        else if (!CheckMergeDiff(false, address)) | 
 |   //        { | 
 |   //            throw (new Exception("Range is not fully merged.Specify the exact range")); | 
 |   //        } | 
 |   //    } | 
 |   //    else | 
 |   //    { | 
 |   //        if (CheckMergeDiff(false, address)) | 
 |   //        { | 
 |   //            SetCellMerge(true, address); | 
 |   //            _worksheet.MergedCells.List.Add(address); | 
 |   //        } | 
 |   //        else | 
 |   //        { | 
 |   //            if (!_worksheet.MergedCells.List.Contains(address)) | 
 |   //            { | 
 |   //                throw (new Exception("Cells are already merged")); | 
 |   //            } | 
 |   //        } | 
 |   //    } | 
 |   //} | 
 |   /// <summary> | 
 |   /// Set an autofilter for the range | 
 |   /// </summary> | 
 |   public bool AutoFilter { | 
 |     get { | 
 |       IsRangeValid("autofilter"); | 
 |       ExcelAddressBase address = _worksheet.AutoFilterAddress; | 
 |       if (address == null) { | 
 |         return false; | 
 |       } | 
 |       if (_fromRow >= address.Start.Row | 
 |           && _toRow <= address.End.Row | 
 |           && _fromCol >= address.Start.Column | 
 |           && _toCol <= address.End.Column) { | 
 |         return true; | 
 |       } | 
 |       return false; | 
 |     } | 
 |     set { | 
 |       IsRangeValid("autofilter"); | 
 |       _worksheet.AutoFilterAddress = this; | 
 |       if (_worksheet.Names.ContainsKey("_xlnm._FilterDatabase")) { | 
 |         _worksheet.Names.Remove("_xlnm._FilterDatabase"); | 
 |       } | 
 |       var result = _worksheet.Names.Add("_xlnm._FilterDatabase", this); | 
 |       result.IsNameHidden = true; | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// If the value is in richtext format. | 
 |   /// </summary> | 
 |   public bool IsRichText { | 
 |     get { | 
 |       IsRangeValid("richtext"); | 
 |       return _worksheet._flags.GetFlagValue(_fromRow, _fromCol, CellFlags.RichText); | 
 |     } | 
 |     set => _changePropMethod(Set_IsRichText, value); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Is the range a part of an Arrayformula | 
 |   /// </summary> | 
 |   public bool IsArrayFormula { | 
 |     get { | 
 |       IsRangeValid("arrayformulas"); | 
 |       return _worksheet._flags.GetFlagValue(_fromRow, _fromCol, CellFlags.ArrayFormula); | 
 |     } | 
 |     set => _changePropMethod(Set_IsArrayFormula, value); | 
 |   } | 
 |  | 
 |   private ExcelRichTextCollection _rtc; | 
 |  | 
 |   /// <summary> | 
 |   /// Cell value is richtext formatted. | 
 |   /// Richtext-property only apply to the left-top cell of the range. | 
 |   /// </summary> | 
 |   public ExcelRichTextCollection RichText { | 
 |     get { | 
 |       IsRangeValid("richtext"); | 
 |       if (_rtc == null) { | 
 |         _rtc = GetRichText(_fromRow, _fromCol); | 
 |       } | 
 |       return _rtc; | 
 |     } | 
 |   } | 
 |  | 
 |   private ExcelRichTextCollection GetRichText(int row, int col) { | 
 |     XmlDocument xml = new XmlDocument(); | 
 |     var v = _worksheet._values.GetValue(row, col); | 
 |     var isRt = _worksheet._flags.GetFlagValue(row, col, CellFlags.RichText); | 
 |     if (v != null) { | 
 |       if (isRt) { | 
 |         XmlHelper.LoadXmlSafe( | 
 |             xml, | 
 |             "<d:si xmlns:d=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" >" | 
 |                 + v | 
 |                 + "</d:si>", | 
 |             Encoding.UTF8); | 
 |       } else { | 
 |         xml.LoadXml( | 
 |             "<d:si xmlns:d=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" ><d:r><d:t>" | 
 |                 + SecurityElement.Escape(v.ToString()) | 
 |                 + "</d:t></d:r></d:si>"); | 
 |       } | 
 |     } else { | 
 |       xml.LoadXml("<d:si xmlns:d=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" />"); | 
 |     } | 
 |     var rtc = new ExcelRichTextCollection( | 
 |         _worksheet.NameSpaceManager, | 
 |         xml.SelectSingleNode("d:si", _worksheet.NameSpaceManager), | 
 |         this); | 
 |     return rtc; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// returns the comment object of the first cell in the range | 
 |   /// </summary> | 
 |   public ExcelComment Comment { | 
 |     get { | 
 |       IsRangeValid("comments"); | 
 |       ulong cellId = GetCellId(_worksheet.SheetID, _fromRow, _fromCol); | 
 |       if (_worksheet.Comments._comments.ContainsKey(cellId)) { | 
 |         return _worksheet._comments._comments[cellId] as ExcelComment; | 
 |       } | 
 |       return null; | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// WorkSheet object | 
 |   /// </summary> | 
 |   public ExcelWorksheet Worksheet => _worksheet; | 
 |  | 
 |   /// <summary> | 
 |   /// Address including sheetname | 
 |   /// </summary> | 
 |   public string FullAddress { | 
 |     get { | 
 |       string fullAddress = GetFullAddress(_worksheet.Name, _address); | 
 |       if (Addresses != null) { | 
 |         foreach (var a in Addresses) { | 
 |           fullAddress += "," + GetFullAddress(_worksheet.Name, a.Address); | 
 |           ; | 
 |         } | 
 |       } | 
 |       return fullAddress; | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Address including sheetname | 
 |   /// </summary> | 
 |   public string FullAddressAbsolute { | 
 |     get { | 
 |       string wbwsRef = string.IsNullOrEmpty(_wb) ? _ws : "[" + _wb.Replace("'", "''") + "]" + _ws; | 
 |       string fullAddress = GetFullAddress( | 
 |           wbwsRef, | 
 |           GetAddress(_fromRow, _fromCol, _toRow, _toCol, true)); | 
 |       if (Addresses != null) { | 
 |         foreach (var a in Addresses) { | 
 |           fullAddress += | 
 |               "," | 
 |                   + GetFullAddress( | 
 |                       wbwsRef, | 
 |                       GetAddress(a.Start.Row, a.Start.Column, a.End.Row, a.End.Column, true)); | 
 |           ; | 
 |         } | 
 |       } | 
 |       return fullAddress; | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Address including sheetname | 
 |   /// </summary> | 
 |   internal string FullAddressAbsoluteNoFullRowCol { | 
 |     get { | 
 |       string wbwsRef = string.IsNullOrEmpty(_wb) ? _ws : "[" + _wb.Replace("'", "''") + "]" + _ws; | 
 |       string fullAddress = GetFullAddress( | 
 |           wbwsRef, | 
 |           GetAddress(_fromRow, _fromCol, _toRow, _toCol, true), | 
 |           false); | 
 |       if (Addresses != null) { | 
 |         foreach (var a in Addresses) { | 
 |           fullAddress += | 
 |               "," | 
 |                   + GetFullAddress( | 
 |                       wbwsRef, | 
 |                       GetAddress(a.Start.Row, a.Start.Column, a.End.Row, a.End.Column, true), | 
 |                       false); | 
 |           ; | 
 |         } | 
 |       } | 
 |       return fullAddress; | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Set the value without altering the richtext property | 
 |   /// </summary> | 
 |   /// <param name="value">the value</param> | 
 |   internal void SetValueRichText(object value) { | 
 |     if (_fromRow == 1 | 
 |         && _fromCol == 1 | 
 |         && _toRow == ExcelPackage.MaxRows | 
 |         && _toCol | 
 |             == ExcelPackage.MaxColumns) //Full sheet (ex ws.Cells.Value=0). Set value for A1 only to avoid hanging | 
 |     { | 
 |       //_worksheet.Cell(1, 1).SetValueRichText(value); | 
 |       SetValue(value, 1, 1); | 
 |     } else { | 
 |       //for (int col = _fromCol; col <= _toCol; col++) | 
 |       //{ | 
 |       //    for (int row = _fromRow; row <= _toRow; row++) | 
 |       //    { | 
 |       //_worksheet.Cell(row, col).SetValueRichText(value); | 
 |       SetValue(value, _fromRow, _fromCol); | 
 |       //} | 
 |       //} | 
 |     } | 
 |   } | 
 |  | 
 |   private void SetValue(object value, int row, int col) { | 
 |     _worksheet.SetValue(row, col, value); | 
 |     // if (value is string) _worksheet._types.SetValue(row, col, "S"); else _worksheet._types.SetValue(row, col, ""); | 
 |     _worksheet._formulas.SetValue(row, col, ""); | 
 |   } | 
 |  | 
 |   internal void SetSharedFormulaId(int id) { | 
 |     for (int col = _fromCol; col <= _toCol; col++) { | 
 |       for (int row = _fromRow; row <= _toRow; row++) { | 
 |         _worksheet._formulas.SetValue(row, col, id); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private void CheckAndSplitSharedFormula(ExcelAddressBase address) { | 
 |     for (int col = address._fromCol; col <= address._toCol; col++) { | 
 |       for (int row = address._fromRow; row <= address._toRow; row++) { | 
 |         var f = _worksheet._formulas.GetValue(row, col); | 
 |         if (f is int i && i >= 0) { | 
 |           SplitFormulas(address); | 
 |           return; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private void SplitFormulas(ExcelAddressBase address) { | 
 |     List<int> formulas = []; | 
 |     for (int col = address._fromCol; col <= address._toCol; col++) { | 
 |       for (int row = address._fromRow; row <= address._toRow; row++) { | 
 |         var f = _worksheet._formulas.GetValue(row, col); | 
 |         if (f is int id) { | 
 |           if (id >= 0 && !formulas.Contains(id)) { | 
 |             if (_worksheet._sharedFormulas[id].IsArray | 
 |                 && Collide(_worksheet.Cells[_worksheet._sharedFormulas[id].Address]) | 
 |                     == eAddressCollition.Partly) // If the formula is an array formula and its on the inside the overwriting range throw an exception | 
 |             { | 
 |               throw (new InvalidOperationException("Can not overwrite a part of an array-formula")); | 
 |             } | 
 |             formulas.Add(id); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     foreach (int ix in formulas) { | 
 |       SplitFormula(address, ix); | 
 |     } | 
 |  | 
 |     ////Clear any formula references inside the refered range | 
 |     //_worksheet._formulas.Clear(address._fromRow, address._toRow, address._toRow - address._fromRow + 1, address._toCol - address.column + 1); | 
 |   } | 
 |  | 
 |   private void SplitFormula(ExcelAddressBase address, int ix) { | 
 |     var f = _worksheet._sharedFormulas[ix]; | 
 |     var fRange = _worksheet.Cells[f.Address]; | 
 |     var collide = address.Collide(fRange); | 
 |  | 
 |     //The formula is inside the currenct range, remove it | 
 |     if (collide == eAddressCollition.Equal || collide == eAddressCollition.Inside) { | 
 |       _worksheet._sharedFormulas.Remove(ix); | 
 |       return; | 
 |       //fRange.SetSharedFormulaID(int.MinValue); | 
 |     } | 
 |     var firstCellCollide = address.Collide( | 
 |         new(fRange._fromRow, fRange._fromCol, fRange._fromRow, fRange._fromCol)); | 
 |     if (collide == eAddressCollition.Partly | 
 |         && (firstCellCollide == eAddressCollition.Inside | 
 |                 || firstCellCollide | 
 |                     == eAddressCollition.Equal)) //Do we need to split? Only if the functions first row is inside the new range. | 
 |     { | 
 |       //The formula partly collides with the current range | 
 |       bool fIsSet = false; | 
 |       string formulaR1C1 = fRange.FormulaR1C1; | 
 |       //Top Range | 
 |       if (fRange._fromRow < _fromRow) { | 
 |         f.Address = GetAddress(fRange._fromRow, fRange._fromCol, _fromRow - 1, fRange._toCol); | 
 |         fIsSet = true; | 
 |       } | 
 |       //Left Range | 
 |       if (fRange._fromCol < address._fromCol) { | 
 |         if (fIsSet) { | 
 |           f = new(SourceCodeTokenizer.Default); | 
 |           f.Index = _worksheet.GetMaxShareFunctionIndex(false); | 
 |           f.StartCol = fRange._fromCol; | 
 |           f.IsArray = false; | 
 |           _worksheet._sharedFormulas.Add(f.Index, f); | 
 |         } else { | 
 |           fIsSet = true; | 
 |         } | 
 |         if (fRange._fromRow < address._fromRow) { | 
 |           f.StartRow = address._fromRow; | 
 |         } else { | 
 |           f.StartRow = fRange._fromRow; | 
 |         } | 
 |         if (fRange._toRow < address._toRow) { | 
 |           f.Address = GetAddress(f.StartRow, f.StartCol, fRange._toRow, address._fromCol - 1); | 
 |         } else { | 
 |           f.Address = GetAddress(f.StartRow, f.StartCol, address._toRow, address._fromCol - 1); | 
 |         } | 
 |         f.Formula = TranslateFromR1C1(formulaR1C1, f.StartRow, f.StartCol); | 
 |         _worksheet.Cells[f.Address].SetSharedFormulaId(f.Index); | 
 |       } | 
 |       //Right Range | 
 |       if (fRange._toCol > address._toCol) { | 
 |         if (fIsSet) { | 
 |           f = new(SourceCodeTokenizer.Default); | 
 |           f.Index = _worksheet.GetMaxShareFunctionIndex(false); | 
 |           f.IsArray = false; | 
 |           _worksheet._sharedFormulas.Add(f.Index, f); | 
 |         } else { | 
 |           fIsSet = true; | 
 |         } | 
 |         f.StartCol = address._toCol + 1; | 
 |         if (address._fromRow < fRange._fromRow) { | 
 |           f.StartRow = fRange._fromRow; | 
 |         } else { | 
 |           f.StartRow = address._fromRow; | 
 |         } | 
 |  | 
 |         if (fRange._toRow < address._toRow) { | 
 |           f.Address = GetAddress(f.StartRow, f.StartCol, fRange._toRow, fRange._toCol); | 
 |         } else { | 
 |           f.Address = GetAddress(f.StartRow, f.StartCol, address._toRow, fRange._toCol); | 
 |         } | 
 |         f.Formula = TranslateFromR1C1(formulaR1C1, f.StartRow, f.StartCol); | 
 |         _worksheet.Cells[f.Address].SetSharedFormulaId(f.Index); | 
 |       } | 
 |       //Bottom Range | 
 |       if (fRange._toRow > address._toRow) { | 
 |         if (fIsSet) { | 
 |           f = new(SourceCodeTokenizer.Default); | 
 |           f.Index = _worksheet.GetMaxShareFunctionIndex(false); | 
 |           f.IsArray = false; | 
 |           _worksheet._sharedFormulas.Add(f.Index, f); | 
 |         } | 
 |  | 
 |         f.StartCol = fRange._fromCol; | 
 |         f.StartRow = _toRow + 1; | 
 |  | 
 |         f.Formula = TranslateFromR1C1(formulaR1C1, f.StartRow, f.StartCol); | 
 |  | 
 |         f.Address = GetAddress(f.StartRow, f.StartCol, fRange._toRow, fRange._toCol); | 
 |         _worksheet.Cells[f.Address].SetSharedFormulaId(f.Index); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private object ConvertData(ExcelTextFormat format, string v, int col, bool isText) { | 
 |     if (isText && (format.DataTypes == null || format.DataTypes.Length < col)) { | 
 |       return v; | 
 |     } | 
 |  | 
 |     double d; | 
 |     DateTime dt; | 
 |     if (format.DataTypes == null | 
 |         || format.DataTypes.Length <= col | 
 |         || format.DataTypes[col] == eDataTypes.Unknown) { | 
 |       string v2 = v.EndsWith("%") ? v.Substring(0, v.Length - 1) : v; | 
 |       if (double.TryParse(v2, NumberStyles.Any, format.Culture, out d)) { | 
 |         if (v2 == v) { | 
 |           return d; | 
 |         } | 
 |         return d / 100; | 
 |       } | 
 |       if (DateTime.TryParse(v, format.Culture, DateTimeStyles.None, out dt)) { | 
 |         return dt; | 
 |       } | 
 |       return v; | 
 |     } | 
 |     switch (format.DataTypes[col]) { | 
 |       case eDataTypes.Number: | 
 |         if (double.TryParse(v, NumberStyles.Any, format.Culture, out d)) { | 
 |           return d; | 
 |         } | 
 |         return v; | 
 |       case eDataTypes.DateTime: | 
 |         if (DateTime.TryParse(v, format.Culture, DateTimeStyles.None, out dt)) { | 
 |           return dt; | 
 |         } | 
 |         return v; | 
 |       case eDataTypes.Percent: | 
 |         string v2 = v.EndsWith("%") ? v.Substring(0, v.Length - 1) : v; | 
 |         if (double.TryParse(v2, NumberStyles.Any, format.Culture, out d)) { | 
 |           return d / 100; | 
 |         } | 
 |         return v; | 
 |  | 
 |       default: | 
 |         return v; | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Conditional Formatting for this range. | 
 |   /// </summary> | 
 |   public IRangeConditionalFormatting ConditionalFormatting => | 
 |     new RangeConditionalFormatting(_worksheet, new(Address)); | 
 |  | 
 |   /// <summary> | 
 |   /// Data validation for this range. | 
 |   /// </summary> | 
 |   public IRangeDataValidation DataValidation => new RangeDataValidation(_worksheet, Address); | 
 |  | 
 |   /// <summary> | 
 |   /// Get the strongly typed value of the cell. | 
 |   /// </summary> | 
 |   /// <typeparam name="T">The type</typeparam> | 
 |   /// <returns>The value. If the value can't be converted to the specified type, the default value will be returned</returns> | 
 |   public T GetValue<T>() { | 
 |     return _worksheet.GetTypedValue<T>(Value); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Get a range with an offset from the top left cell. | 
 |   /// The new range has the same dimensions as the current range | 
 |   /// </summary> | 
 |   /// <param name="rowOffset">Row Offset</param> | 
 |   /// <param name="columnOffset">Column Offset</param> | 
 |   /// <returns></returns> | 
 |   public ExcelRangeBase Offset(int rowOffset, int columnOffset) { | 
 |     if (_fromRow + rowOffset < 1 | 
 |         || _fromCol + columnOffset < 1 | 
 |         || _fromRow + rowOffset > ExcelPackage.MaxRows | 
 |         || _fromCol + columnOffset > ExcelPackage.MaxColumns) { | 
 |       throw (new ArgumentOutOfRangeException("Offset value out of range")); | 
 |     } | 
 |     string address = GetAddress( | 
 |         _fromRow + rowOffset, | 
 |         _fromCol + columnOffset, | 
 |         _toRow + rowOffset, | 
 |         _toCol + columnOffset); | 
 |     return new(_worksheet, address); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Get a range with an offset from the top left cell. | 
 |   /// </summary> | 
 |   /// <param name="rowOffset">Row Offset</param> | 
 |   /// <param name="columnOffset">Column Offset</param> | 
 |   /// <param name="numberOfRows">Number of rows. Minimum 1</param> | 
 |   /// <param name="numberOfColumns">Number of colums. Minimum 1</param> | 
 |   /// <returns></returns> | 
 |   public ExcelRangeBase Offset( | 
 |       int rowOffset, | 
 |       int columnOffset, | 
 |       int numberOfRows, | 
 |       int numberOfColumns) { | 
 |     if (numberOfRows < 1 || numberOfColumns < 1) { | 
 |       throw (new("Number of rows/columns must be greater than 0")); | 
 |     } | 
 |     numberOfRows--; | 
 |     numberOfColumns--; | 
 |     if (_fromRow + rowOffset < 1 | 
 |         || _fromCol + columnOffset < 1 | 
 |         || _fromRow + rowOffset > ExcelPackage.MaxRows | 
 |         || _fromCol + columnOffset > ExcelPackage.MaxColumns | 
 |         || _fromRow + rowOffset + numberOfRows < 1 | 
 |         || _fromCol + columnOffset + numberOfColumns < 1 | 
 |         || _fromRow + rowOffset + numberOfRows > ExcelPackage.MaxRows | 
 |         || _fromCol + columnOffset + numberOfColumns > ExcelPackage.MaxColumns) { | 
 |       throw (new ArgumentOutOfRangeException("Offset value out of range")); | 
 |     } | 
 |     string address = GetAddress( | 
 |         _fromRow + rowOffset, | 
 |         _fromCol + columnOffset, | 
 |         _fromRow + rowOffset + numberOfRows, | 
 |         _fromCol + columnOffset + numberOfColumns); | 
 |     return new(_worksheet, address); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Clear all cells | 
 |   /// </summary> | 
 |   public void Clear() { | 
 |     Delete(this, false); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Creates an array-formula. | 
 |   /// </summary> | 
 |   /// <param name="arrayFormula">The formula</param> | 
 |   public void CreateArrayFormula(string arrayFormula) { | 
 |     if (Addresses != null) { | 
 |       throw (new("An Arrayformula can not have more than one address")); | 
 |     } | 
 |     Set_SharedFormula(arrayFormula, this, true); | 
 |   } | 
 |  | 
 |   //private void Clear(ExcelAddressBase Range) | 
 |   //{ | 
 |   //    Clear(Range, true); | 
 |   //} | 
 |   internal void Delete(ExcelAddressBase range, bool shift) { | 
 |     //DeleteCheckMergedCells(Range); | 
 |     _worksheet.MergedCells.Clear(range); | 
 |     //First find the start cell | 
 |     int fromRow, | 
 |         fromCol; | 
 |     var d = Worksheet.Dimension; | 
 |     if (d != null | 
 |         && range._fromRow <= d._fromRow | 
 |         && range._toRow | 
 |             >= d._toRow) //EntireRow? | 
 |     { | 
 |       fromRow = 0; | 
 |     } else { | 
 |       fromRow = range._fromRow; | 
 |     } | 
 |     if (d != null | 
 |         && range._fromCol <= d._fromCol | 
 |         && range._toCol | 
 |             >= d._toCol) //EntireRow? | 
 |     { | 
 |       fromCol = 0; | 
 |     } else { | 
 |       fromCol = range._fromCol; | 
 |     } | 
 |  | 
 |     var rows = range._toRow - fromRow + 1; | 
 |     var cols = range._toCol - fromCol + 1; | 
 |  | 
 |     _worksheet._values.Delete(fromRow, fromCol, rows, cols, shift); | 
 |     _worksheet._types.Delete(fromRow, fromCol, rows, cols, shift); | 
 |     _worksheet._styles.Delete(fromRow, fromCol, rows, cols, shift); | 
 |     _worksheet._formulas.Delete(fromRow, fromCol, rows, cols, shift); | 
 |     _worksheet._hyperLinks.Delete(fromRow, fromCol, rows, cols, shift); | 
 |     _worksheet._flags.Delete(fromRow, fromCol, rows, cols, shift); | 
 |     _worksheet._commentsStore.Delete(fromRow, fromCol, rows, cols, shift); | 
 |  | 
 |     //if(shift) | 
 |     //{ | 
 |     //    _worksheet.AdjustFormulasRow(fromRow, rows); | 
 |     //} | 
 |  | 
 |     //Clear multi addresses as well | 
 |     if (Addresses != null) { | 
 |       foreach (var sub in Addresses) { | 
 |         Delete(sub, shift); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   public void Dispose() {} | 
 |  | 
 |   //int _index; | 
 |   //ulong _toCellId; | 
 |   //int _enumAddressIx; | 
 |   private CellsStoreEnumerator<object> cellEnum; | 
 |  | 
 |   public IEnumerator<ExcelRangeBase> GetEnumerator() { | 
 |     Reset(); | 
 |     return this; | 
 |   } | 
 |  | 
 |   IEnumerator IEnumerable.GetEnumerator() { | 
 |     Reset(); | 
 |     return this; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// The current range when enumerating | 
 |   /// </summary> | 
 |   public ExcelRangeBase Current => new(_worksheet, GetAddress(cellEnum.Row, cellEnum.Column)); | 
 |  | 
 |   /// <summary> | 
 |   /// The current range when enumerating | 
 |   /// </summary> | 
 |   object IEnumerator.Current => | 
 |     new ExcelRangeBase(_worksheet, GetAddress(cellEnum.Row, cellEnum.Column)); | 
 |  | 
 |   private int _enumAddressIx = -1; | 
 |  | 
 |   public bool MoveNext() { | 
 |     if (cellEnum.Next()) { | 
 |       return true; | 
 |     } | 
 |     if (_addresses != null) { | 
 |       _enumAddressIx++; | 
 |       if (_enumAddressIx < _addresses.Count) { | 
 |         cellEnum = new( | 
 |             _worksheet._values, | 
 |             _addresses[_enumAddressIx]._fromRow, | 
 |             _addresses[_enumAddressIx]._fromCol, | 
 |             _addresses[_enumAddressIx]._toRow, | 
 |             _addresses[_enumAddressIx]._toCol); | 
 |         return MoveNext(); | 
 |       } | 
 |       return false; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   public void Reset() { | 
 |     _enumAddressIx = -1; | 
 |     cellEnum = new(_worksheet._values, _fromRow, _fromCol, _toRow, _toCol); | 
 |   } | 
 |  | 
 |   //private void GetNextIndexEnum(int fromRow, int fromCol, int toRow, int toCol) | 
 |   //{ | 
 |   //    if (_index >= _worksheet._cells.Count) return; | 
 |   //    ExcelCell cell = _worksheet._cells[_index] as ExcelCell; | 
 |   //    while (cell.Column > toCol || cell.Column < fromCol) | 
 |   //    { | 
 |   //        if (cell.Column < fromCol) | 
 |   //        { | 
 |   //            _index = _worksheet._cells.IndexOf(ExcelAddress.GetCellID(_worksheet.SheetID, cell.Row, fromCol)); | 
 |   //        } | 
 |   //        else | 
 |   //        { | 
 |   //            _index = _worksheet._cells.IndexOf(ExcelAddress.GetCellID(_worksheet.SheetID, cell.Row + 1, fromCol)); | 
 |   //        } | 
 |  | 
 |   //        if (_index < 0) | 
 |   //        { | 
 |   //            _index = ~_index; | 
 |   //        } | 
 |   //        if (_index >= _worksheet._cells.Count || _worksheet._cells[_index].RangeID > _toCellId) | 
 |   //        { | 
 |   //            break; | 
 |   //        } | 
 |   //        cell = _worksheet._cells[_index] as ExcelCell; | 
 |   //    } | 
 |   //} | 
 |  | 
 |   //private void GetStartIndexEnum(int fromRow, int fromCol, int toRow, int toCol) | 
 |   //{ | 
 |   //    _index = _worksheet._cells.IndexOf(ExcelCellBase.GetCellID(_worksheet.SheetID, fromRow, fromCol)); | 
 |   //    _toCellId = ExcelCellBase.GetCellID(_worksheet.SheetID, toRow, toCol); | 
 |   //    if (_index < 0) | 
 |   //    { | 
 |   //        _index = ~_index; | 
 |   //    } | 
 |   //    _index--; | 
 |   //} | 
 | } |