/******************************************************************************* * 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.Generic; using System.ComponentModel; using System.Text; using System.Data; using OfficeOpenXml.FormulaParsing; using OfficeOpenXml.Style; using System.Xml; using System.Drawing; using System.Globalization; using System.Collections; using OfficeOpenXml.Table; using System.Text.RegularExpressions; using System.IO; using System.Linq; using OfficeOpenXml.DataValidation; using OfficeOpenXml.DataValidation.Contracts; using System.Reflection; using OfficeOpenXml.Style.XmlAccess; using System.Security; using OfficeOpenXml.ConditionalFormatting; using OfficeOpenXml.ConditionalFormatting.Contracts; using OfficeOpenXml.FormulaParsing.LexicalAnalysis; namespace OfficeOpenXml { /// /// A range of cells /// public class ExcelRangeBase : ExcelAddress, IExcelCell, IEnumerable { /// /// Reference to the worksheet /// protected ExcelWorksheet _worksheet; internal ExcelWorkbook _workbook = null; private delegate void _changeProp(_setValue method, object value); private delegate void _setValue(object value, int row, int col); private _changeProp _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; } //} #region Constructors internal ExcelRangeBase(ExcelWorksheet xlWorksheet) { _worksheet = xlWorksheet; _ws = _worksheet.Name; _workbook = _worksheet.Workbook; this.AddressChange += new EventHandler(ExcelRangeBase_AddressChange); SetDelegate(); } void ExcelRangeBase_AddressChange(object sender, EventArgs e) { if (Table != null) { SetRCFromTable(_workbook._package, null); } SetDelegate(); } internal ExcelRangeBase(ExcelWorksheet xlWorksheet, string address) : base(xlWorksheet == null ? "" : xlWorksheet.Name, address) { _worksheet = xlWorksheet; _workbook = _worksheet.Workbook; base.SetRCFromTable(_worksheet._package, null); if (string.IsNullOrEmpty(_ws)) _ws = _worksheet == null ? "" : _worksheet.Name; this.AddressChange += new EventHandler(ExcelRangeBase_AddressChange); SetDelegate(); } internal ExcelRangeBase(ExcelWorkbook wb, ExcelWorksheet xlWorksheet, string address, bool isName) : base(xlWorksheet == null ? "" : xlWorksheet.Name, address, isName) { SetRCFromTable(wb._package, null); _worksheet = xlWorksheet; _workbook = wb; if (string.IsNullOrEmpty(_ws)) _ws = (xlWorksheet == null ? null : xlWorksheet.Name); this.AddressChange += new EventHandler(ExcelRangeBase_AddressChange); SetDelegate(); } ~ExcelRangeBase() { this.AddressChange -= new EventHandler(ExcelRangeBase_AddressChange); } #endregion #region Set Value Delegates 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; } } /// /// We dont know the address yet. Set the delegate first time a property is set. /// /// /// private void SetUnknown(_setValue valueMethod, object value) { //Address is not set use, selected range if (_fromRow == -1) { SetToSelectedRange(); } SetDelegate(); _changePropMethod(valueMethod, value); } /// /// Set a single cell /// /// /// private void SetSingle(_setValue valueMethod, object value) { valueMethod(value, _fromRow, _fromCol); } /// /// Set a range /// /// /// private void SetRange(_setValue valueMethod, object value) { SetValueAddress(this, valueMethod, value); } /// /// Set a multirange (A1:A2,C1:C2) /// /// /// private void SetMultiRange(_setValue valueMethod, object value) { SetValueAddress(this, valueMethod, value); foreach (var address in Addresses) { SetValueAddress(address, valueMethod, value); } } /// /// Set the property for an address /// /// /// /// private void SetValueAddress(ExcelAddress address, _setValue 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")); } else { 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); } } } } #endregion #region Set property methods private void Set_StyleID(object value, int row, int col) { _worksheet._styles.SetValue(row, col, (int)value); } private void Set_StyleName(object value, int row, int col) { //_worksheet.Cell(row, col).SetNewStyleName(value.ToString(), _styleID); _worksheet._styles.SetValue(row, col, _styleID); } 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 && (int)f >= 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); } } /// /// Handles shared formulas /// /// The formula /// The address of the formula /// If the forumla is an array formula. 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")); } else 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) { _worksheet._hyperLinks.SetValue(row, col, (Uri)value); if (value is ExcelHyperLink) { _worksheet._values.SetValue(row, col, ((ExcelHyperLink)value).Display); } else { _worksheet._values.SetValue(row, col, ((Uri)value).OriginalString); } } else { _worksheet._hyperLinks.SetValue(row, col, (Uri)null); _worksheet._values.SetValue(row, col, (Uri)null); } } 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 Set_Comment(object value, int row, int col) { string[] v = (string[])value; Worksheet.Comments.Add(new ExcelRangeBase(_worksheet, GetAddress(_fromRow, _fromCol)), v[0], v[1]); // _worksheet.Cell(row, col).Comment = comment; } #endregion 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))); } else { throw (new InvalidOperationException(string.Format("Range is not valid for {0} : {1}", type, _address))); } } } } #region Public Properties /// /// The styleobject for the range. /// 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); } } /// /// The named style /// 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(_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; } else { 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(_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(_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; } else { 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; } /// /// 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. /// 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); } } /// /// Set the range to a specific value /// public object Value { get { if (IsName) { if (_worksheet == null) { return _workbook._names[_address].NameValue; } else { return _worksheet.Names[_address].NameValue; } } else { if (_fromRow == _toRow && _fromCol == _toCol) { return _worksheet.GetValue(_fromRow, _fromCol); } else { 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 bool IsInfinityValue(object value) { double? valueAsDouble = value as double?; if(valueAsDouble.HasValue && (double.IsNegativeInfinity(valueAsDouble.Value) || double.IsPositiveInfinity(valueAsDouble.Value))) { return true; } return false; } 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 (IsRichText) { 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; } private ExcelAddressBase GetAddressDim(ExcelRangeBase addr) { int fromRow, fromCol, toRow, toCol; var d = _worksheet.Dimension; fromRow = addr._fromRow < d._fromRow ? d._fromRow : addr._fromRow; fromCol = addr._fromCol < d._fromCol ? d._fromCol : addr._fromCol; toRow = addr._toRow > d._toRow ? d._toRow : addr._toRow; toCol = addr._toCol > d._toCol ? d._toCol : addr._toCol; if (addr._fromCol == fromRow && addr._fromCol == addr._fromCol && addr._toRow == toRow && addr._toCol == _toCol) { return addr; } else { if (_fromRow > _toRow || _fromCol > _toCol) { return null; } else { return new ExcelAddressBase(fromRow, fromCol, toRow, toCol); } } } private object GetSingleValue() { if (IsRichText) { return RichText.Text; } else { return _worksheet._values.GetValue(_fromRow, _fromCol); } } /// /// Returns the formatted value. /// public string Text { get { return GetFormattedText(false); } } /// /// Set the column width from the content of the range. The minimum width is the value of the ExcelWorksheet.defaultColumnWidth property. /// Note: Cells containing formulas are ignored since EPPlus don't have a calculation engine. /// Wrapped and merged cells are also ignored. /// public void AutoFitColumns() { AutoFitColumns(_worksheet.DefaultColWidth); } /// /// Set the column width from the content of the range. /// Note: Cells containing formulas are ignored if no calculation is made. /// Wrapped and merged cells are also ignored. /// /// This method will not work if you run in an environment that does not support GDI /// Minimum column width public void AutoFitColumns(double MinimumWidth) { AutoFitColumns(MinimumWidth, double.MaxValue); } /// /// Set the column width from the content of the range. /// Note: Cells containing formulas are ignored if no calculation is made. /// Wrapped and merged cells are also ignored. /// /// Minimum column width /// Maximum column width public void AutoFitColumns(double MinimumWidth, double MaximumWidth) { if (_worksheet.Dimension == null) { return; } if (_fromCol < 1 || _fromRow < 1) { SetToSelectedRange(); } var fontCache = new Dictionary(); bool doAdjust = _worksheet._package.DoAdjustDrawings; _worksheet._package.DoAdjustDrawings = false; var drawWidths = _worksheet.Drawings.GetDrawingWidths(); var fromCol = _fromCol > _worksheet.Dimension._fromCol ? _fromCol : _worksheet.Dimension._fromCol; var toCol = _toCol < _worksheet.Dimension._toCol ? _toCol : _worksheet.Dimension._toCol; if (Addresses == null) { SetMinWidth(MinimumWidth, fromCol, toCol); } else { foreach (var addr in Addresses) { fromCol = addr._fromCol > _worksheet.Dimension._fromCol ? addr._fromCol : _worksheet.Dimension._fromCol; toCol = addr._toCol < _worksheet.Dimension._toCol ? addr._toCol : _worksheet.Dimension._toCol; SetMinWidth(MinimumWidth, fromCol, toCol); } } //Get any autofilter to widen these columns var afAddr = new List(); if (_worksheet.AutoFilterAddress != null) { afAddr.Add(new ExcelAddressBase( _worksheet.AutoFilterAddress._fromRow, _worksheet.AutoFilterAddress._fromCol, _worksheet.AutoFilterAddress._fromRow, _worksheet.AutoFilterAddress._toCol)); afAddr[afAddr.Count - 1]._ws = WorkSheet; } foreach (var tbl in _worksheet.Tables) { if (tbl.AutoFilterAddress != null) { afAddr.Add(new ExcelAddressBase(tbl.AutoFilterAddress._fromRow, tbl.AutoFilterAddress._fromCol, tbl.AutoFilterAddress._fromRow, tbl.AutoFilterAddress._toCol)); afAddr[afAddr.Count - 1]._ws = WorkSheet; } } var styles = _worksheet.Workbook.Styles; var nf = styles.Fonts[styles.CellXfs[0].FontId]; var fs = FontStyle.Regular; if (nf.Bold) fs |= FontStyle.Bold; if (nf.UnderLine) fs |= FontStyle.Underline; if (nf.Italic) fs |= FontStyle.Italic; if (nf.Strike) fs |= FontStyle.Strikeout; var nfont = new Font(nf.Name, nf.Size, fs); using (var b = new Bitmap(1, 1)) { using (var g = Graphics.FromImage(b)) { var normalSize = (float)Math.Truncate(g.MeasureString("00", nfont).Width - g.MeasureString("0", nfont).Width); g.PageUnit = GraphicsUnit.Pixel; foreach (var cell in this) { if (cell.Merge == true || cell.Style.WrapText) continue; var fntID = styles.CellXfs[cell.StyleID].FontId; Font f; if (fontCache.ContainsKey(fntID)) { f = fontCache[fntID]; } else { var fnt = styles.Fonts[fntID]; fs = FontStyle.Regular; if (fnt.Bold) fs |= FontStyle.Bold; if (fnt.UnderLine) fs |= FontStyle.Underline; if (fnt.Italic) fs |= FontStyle.Italic; if (fnt.Strike) fs |= FontStyle.Strikeout; f = new Font(fnt.Name, fnt.Size, fs); fontCache.Add(fntID, f); } //Truncate(({pixels}-5)/{Maximum Digit Width} * 100+0.5)/100 var size = g.MeasureString(cell.TextForWidth, f); double width; double r = styles.CellXfs[cell.StyleID].TextRotation; if (r <= 0 ) { width = (size.Width + 5) / normalSize; } else { r = (r <= 90 ? r : r - 90); width = (((size.Width - size.Height) * Math.Abs(System.Math.Cos(System.Math.PI * r / 180.0)) + size.Height) + 5) / normalSize; } foreach (var a in afAddr) { if (a.Collide(cell) != eAddressCollition.No) { width += 2.25; break; } } if (width > _worksheet.Column(cell._fromCol).Width) { _worksheet.Column(cell._fromCol).Width = width > MaximumWidth ? MaximumWidth : width; } } } } _worksheet.Drawings.AdjustWidth(drawWidths); _worksheet._package.DoAdjustDrawings = doAdjust; } private void SetMinWidth(double minimumWidth, int fromCol, int toCol) { var iterator = new CellsStoreEnumerator(_worksheet._values, 0, fromCol, 0, toCol); var prevCol = fromCol; foreach (ExcelColumn col in iterator) { col.Width = minimumWidth; if (_worksheet.DefaultColWidth > minimumWidth && col.ColumnMin > prevCol) { var newCol = _worksheet.Column(prevCol); newCol.ColumnMax = col.ColumnMin - 1; newCol.Width = minimumWidth; } prevCol = col.ColumnMax + 1; } if (_worksheet.DefaultColWidth > minimumWidth && prevCol /// Gets or sets a formula for a range. /// public string Formula { get { if (IsName) { if (_worksheet == null) { return _workbook._names[_address].NameFormula; } else { return _worksheet.Names[_address].NameFormula; } } else { 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); } } } } } } /// /// Gets or Set a formula in R1C1 format. /// 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[ExcelCellBase.TranslateFromR1C1(value, _fromRow, _fromCol)].Value = null; } else if (Addresses == null) { Set_SharedFormula(ExcelCellBase.TranslateFromR1C1(value, _fromRow, _fromCol), this, false); } else { Set_SharedFormula(ExcelCellBase.TranslateFromR1C1(value, _fromRow, _fromCol), new ExcelAddress(WorkSheet, FirstAddress), false); foreach (var address in Addresses) { Set_SharedFormula(ExcelCellBase.TranslateFromR1C1(value, address.Start.Row, address.Start.Column), address, false); } } } } /// /// Set the hyperlink property for a range of cells /// public Uri Hyperlink { get { IsRangeValid("formulaR1C1"); return _worksheet._hyperLinks.GetValue(_fromRow, _fromCol); } set { _changePropMethod(Set_HyperLink, value); } } /// /// If the cells in the range are merged. /// 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 ExcelAddressBase(FirstAddress), true); if (Addresses != null) { foreach (var address in Addresses) { _worksheet.MergedCells.Add(address, true); //SetMerge(value, address._address); } } } else { _worksheet.MergedCells.Delete(this); if (Addresses != null) { foreach (var address in Addresses) { _worksheet.MergedCells.Delete(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")); // } // } // } //} /// /// Set an autofilter for the range /// 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; } } /// /// If the value is in richtext format. /// public bool IsRichText { get { IsRangeValid("richtext"); return _worksheet._flags.GetFlagValue(_fromRow, _fromCol,CellFlags.RichText); } set { _changePropMethod(Set_IsRichText, value); } } /// /// Is the range a part of an Arrayformula /// public bool IsArrayFormula { get { IsRangeValid("arrayformulas"); return _worksheet._flags.GetFlagValue(_fromRow, _fromCol, CellFlags.ArrayFormula); } } ExcelRichTextCollection _rtc = null; /// /// Cell value is richtext formated. /// 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, "" + v.ToString() + "", Encoding.UTF8); } else { xml.LoadXml("" + SecurityElement.Escape(v.ToString()) + ""); } } else { xml.LoadXml(""); } var rtc = new ExcelRichTextCollection(_worksheet.NameSpaceManager, xml.SelectSingleNode("d:si", _worksheet.NameSpaceManager), this); if (rtc.Count == 1 && isRt == false) { IsRichText = true; var s = _worksheet._styles.GetValue(row, col); //var fnt = cell.Style.Font; var fnt = _worksheet.Workbook.Styles.GetStyleObject(s, _worksheet.PositionID, ExcelAddressBase.GetAddress(row, col)).Font; rtc[0].PreserveSpace = true; rtc[0].Bold = fnt.Bold; rtc[0].FontName = fnt.Name; rtc[0].Italic = fnt.Italic; rtc[0].Size = fnt.Size; rtc[0].UnderLine = fnt.UnderLine; int hex; if (fnt.Color.Rgb != "" && int.TryParse(fnt.Color.Rgb, NumberStyles.HexNumber, null, out hex)) { rtc[0].Color = Color.FromArgb(hex); } } return rtc; } /// /// returns the comment object of the first cell in the range /// 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; } } /// /// WorkSheet object /// public ExcelWorksheet Worksheet { get { return _worksheet; } } /// /// Address including sheetname /// 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; } } /// /// Address including sheetname /// public string FullAddressAbsolute { get { string wbwsRef = string.IsNullOrEmpty(base._wb) ? base._ws : "[" + base._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; } } /// /// Address including sheetname /// internal string FullAddressAbsoluteNoFullRowCol { get { string wbwsRef = string.IsNullOrEmpty(base._wb) ? base._ws : "[" + base._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; } } #endregion #region Private Methods ///// ///// Check if the range is partly merged ///// ///// the starting value ///// the address ///// //private bool CheckMergeDiff(bool startValue, string address) //{ // ExcelAddress a = new ExcelAddress(address); // for (int col = a.column; col <= a._toCol; col++) // { // for (int row = a._fromRow; row <= a._toRow; row++) // { // if (_worksheet._flags.GetFlagValue(row, col, CellFlags.Merged) != startValue) // { // return false; // } // } // } // return true; //} ///// ///// Set the merge flag for the range ///// ///// ///// //internal void SetCellMerge(bool value, string address) //{ // ExcelAddress a = new ExcelAddress(address); // for (int col = a.column; col <= a._toCol; col++) // { // for (int row = a._fromRow; row <= a._toRow; row++) // { // _worksheet._flags.SetFlagValue(row, col,value,CellFlags.Merged); // } // } //} /// /// Set the value without altering the richtext property /// /// the value 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, row,col); } } } } 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, ""); } /// /// Removes a shared formula /// //private void RemoveFormuls(ExcelAddress address) //{ // List removed = new List(); // int fFromRow, fFromCol, fToRow, fToCol; // foreach (int index in _worksheet._sharedFormulas.Keys) // { // ExcelWorksheet.Formulas f = _worksheet._sharedFormulas[index]; // ExcelCellBase.GetRowColFromAddress(f.Address, out fFromRow, out fFromCol, out fToRow, out fToCol); // if (((fFromCol >= address.Start.Column && fFromCol <= address.End.Column) || // (fToCol >= address.Start.Column && fToCol <= address.End.Column)) && // ((fFromRow >= address.Start.Row && fFromRow <= address.End.Row) || // (fToRow >= address.Start.Row && fToRow <= address.End.Row))) // { // for (int col = fFromCol; col <= fToCol; col++) // { // for (int row = fFromRow; row <= fToRow; row++) // { // _worksheet._formulas.SetValue(row, col, int.MinValue); // } // } // removed.Add(index); // } // } // foreach (int index in removed) // { // _worksheet._sharedFormulas.Remove(index); // } //} 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 && (int)f >= 0) { SplitFormulas(address); return; } } } } private void SplitFormulas(ExcelAddressBase address) { List formulas = new List(); 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) { int id = (int)f; 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); } ////Delete any formula references inside the refered range //_worksheet._formulas.Delete(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 ExcelAddressBase(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 = ExcelCellBase.GetAddress(fRange._fromRow, fRange._fromCol, _fromRow - 1, fRange._toCol); fIsSet = true; } //Left Range if (fRange._fromCol < address._fromCol) { if (fIsSet) { f = new ExcelWorksheet.Formulas(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 = ExcelCellBase.GetAddress(f.StartRow, f.StartCol, fRange._toRow, address._fromCol - 1); } else { f.Address = ExcelCellBase.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 ExcelWorksheet.Formulas(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 = ExcelCellBase.GetAddress(f.StartRow, f.StartCol, fRange._toRow, fRange._toCol); } else { f.Address = ExcelCellBase.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 ExcelWorksheet.Formulas(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 = ExcelCellBase.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; } else { return d / 100; } } if (DateTime.TryParse(v, Format.Culture, DateTimeStyles.None, out dt)) { return dt; } else { return v; } } else { switch (Format.DataTypes[col]) { case eDataTypes.Number: if (double.TryParse(v, NumberStyles.Any, Format.Culture, out d)) { return d; } else { return v; } case eDataTypes.DateTime: if (DateTime.TryParse(v, Format.Culture, DateTimeStyles.None, out dt)) { return dt; } else { 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; } else { return v; } default: return v; } } } #endregion #region Public Methods #region ConditionalFormatting /// /// Conditional Formatting for this range. /// public IRangeConditionalFormatting ConditionalFormatting { get { return new RangeConditionalFormatting(_worksheet, new ExcelAddress(Address)); } } #endregion #region DataValidation /// /// Data validation for this range. /// public IRangeDataValidation DataValidation { get { return new RangeDataValidation(_worksheet, Address); } } #endregion #region LoadFromDataReader /// /// Load the data from the datareader starting from the top left cell of the range /// /// The datareader to loadfrom /// Print the column caption property (if set) or the columnname property if not, on first row /// The name of the table /// The table style to apply to the data /// The filled range public ExcelRangeBase LoadFromDataReader(IDataReader Reader, bool PrintHeaders, string TableName, TableStyles TableStyle = TableStyles.None) { var r = LoadFromDataReader(Reader, PrintHeaders); int rows = r.Rows - 1; if (rows >= 0 && r.Columns > 0) { var tbl = _worksheet.Tables.Add(new ExcelAddressBase(_fromRow, _fromCol, _fromRow + (rows <= 0 ? 1 : rows), _fromCol + r.Columns - 1), TableName); tbl.ShowHeader = PrintHeaders; tbl.TableStyle = TableStyle; } return r; } /// /// Load the data from the datareader starting from the top left cell of the range /// /// The datareader to load from /// Print the caption property (if set) or the columnname property if not, on first row /// The filled range public ExcelRangeBase LoadFromDataReader(IDataReader Reader, bool PrintHeaders) { if (Reader == null) { throw (new ArgumentNullException("Reader", "Reader can't be null")); } int fieldCount = Reader.FieldCount; int col = _fromCol, row = _fromRow; if (PrintHeaders) { for (int i = 0; i < fieldCount; i++) { // If no caption is set, the ColumnName property is called implicitly. _worksheet._values.SetValue(row, col++, Reader.GetName(i)); } row++; col = _fromCol; } while(Reader.Read()) { for (int i = 0; i < fieldCount; i++) { _worksheet._values.SetValue(row, col++, Reader.GetValue(i)); } row++; col = _fromCol; } return _worksheet.Cells[_fromRow, _fromCol, row - 1, _fromCol + fieldCount - 1]; } #endregion #region LoadFromDataTable /// /// Load the data from the datatable starting from the top left cell of the range /// /// The datatable to load /// Print the column caption property (if set) or the columnname property if not, on first row /// The table style to apply to the data /// The filled range public ExcelRangeBase LoadFromDataTable(DataTable Table, bool PrintHeaders, TableStyles TableStyle) { var r = LoadFromDataTable(Table, PrintHeaders); int rows = (Table.Rows.Count == 0 ? 1 : Table.Rows.Count) + (PrintHeaders ? 1 : 0); if (rows >= 0 && Table.Columns.Count>0) { var tbl = _worksheet.Tables.Add(new ExcelAddressBase(_fromRow, _fromCol, _fromRow + rows - 1, _fromCol + Table.Columns.Count-1), Table.TableName); tbl.ShowHeader = PrintHeaders; tbl.TableStyle = TableStyle; } return r; } /// /// Load the data from the datatable starting from the top left cell of the range /// /// The datatable to load /// Print the caption property (if set) or the columnname property if not, on first row /// The filled range public ExcelRangeBase LoadFromDataTable(DataTable Table, bool PrintHeaders) { if (Table == null) { throw (new ArgumentNullException("Table can't be null")); } int col = _fromCol, row = _fromRow; if (PrintHeaders) { foreach (DataColumn dc in Table.Columns) { // If no caption is set, the ColumnName property is called implicitly. _worksheet._values.SetValue(row, col++, dc.Caption); } row++; col = _fromCol; } foreach (DataRow dr in Table.Rows) { foreach (object value in dr.ItemArray) { if (value != null && value != DBNull.Value && !string.IsNullOrEmpty(value.ToString())) { _worksheet._values.SetValue(row, col++, value); } else { col++; } } row++; col = _fromCol; } return _worksheet.Cells[_fromRow, _fromCol, (row == _fromRow ? _fromRow : row - 1), _fromCol + Table.Columns.Count - 1]; } #endregion #region LoadFromArrays /// /// Loads data from the collection of arrays of objects into the range, starting from /// the top-left cell. /// /// The data. public ExcelRangeBase LoadFromArrays(IEnumerable Data) { //thanx to Abdullin for the code contribution if (Data == null) throw new ArgumentNullException("data"); int column = _fromCol, row = _fromRow; foreach (var rowData in Data) { column = _fromCol; foreach (var cellData in rowData) { _worksheet._values.SetValue(row, column, cellData); column += 1; } row += 1; } return _worksheet.Cells[_fromRow, _fromCol, row - 1, column - 1]; } #endregion #region LoadFromCollection /// /// Load a collection into a the worksheet starting from the top left row of the range. /// /// The datatype in the collection /// The collection to load /// The filled range public ExcelRangeBase LoadFromCollection(IEnumerable Collection) { return LoadFromCollection(Collection, false, TableStyles.None, BindingFlags.Public | BindingFlags.Instance, null); } /// /// Load a collection of T into the worksheet starting from the top left row of the range. /// Default option will load all public instance properties of T /// /// The datatype in the collection /// The collection to load /// Print the property names on the first row. If the property is decorated with a or a that attribute will be used instead of the reflected member name. /// The filled range public ExcelRangeBase LoadFromCollection(IEnumerable Collection, bool PrintHeaders) { return LoadFromCollection(Collection, PrintHeaders, TableStyles.None, BindingFlags.Public | BindingFlags.Instance, null); } /// /// Load a collection of T into the worksheet starting from the top left row of the range. /// Default option will load all public instance properties of T /// /// The datatype in the collection /// The collection to load /// Print the property names on the first row. If the property is decorated with a or a that attribute will be used instead of the reflected member name. /// Will create a table with this style. If set to TableStyles.None no table will be created /// The filled range public ExcelRangeBase LoadFromCollection(IEnumerable Collection, bool PrintHeaders, TableStyles TableStyle) { return LoadFromCollection(Collection, PrintHeaders, TableStyle, BindingFlags.Public | BindingFlags.Instance, null); } /// /// Load a collection into the worksheet starting from the top left row of the range. /// /// The datatype in the collection /// The collection to load /// Print the property names on the first row. Any underscore in the property name will be converted to a space. If the property is decorated with a or a that attribute will be used instead of the reflected member name. /// Will create a table with this style. If set to TableStyles.None no table will be created /// Property flags to use /// The properties to output. Must be of type T /// The filled range public ExcelRangeBase LoadFromCollection(IEnumerable Collection, bool PrintHeaders, TableStyles TableStyle, BindingFlags memberFlags, MemberInfo[] Members) { var type = typeof(T); if (Members == null) { Members = type.GetProperties(memberFlags); } else { foreach (var t in Members) { if (t.DeclaringType!=null && t.DeclaringType != type && !t.DeclaringType.IsSubclassOf(type)) { throw new InvalidCastException("Supplied properties in parameter Properties must be of the same type as T (or an assignable type from T"); } } } int col = _fromCol, row = _fromRow; if (Members.Length > 0 && PrintHeaders) { foreach (var t in Members) { var descriptionAttribute = t.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute; var header = string.Empty; if (descriptionAttribute != null) { header = descriptionAttribute.Description; } else { var displayNameAttribute = t.GetCustomAttributes(typeof (DisplayNameAttribute), false).FirstOrDefault() as DisplayNameAttribute; if (displayNameAttribute != null) { header = displayNameAttribute.DisplayName; } else { header = t.Name.Replace('_', ' '); } } _worksheet._values.SetValue(row, col++, header); } row++; } if (Members.Length == 0) { foreach (var item in Collection) { _worksheet.Cells[row++, col].Value = item; } } else { foreach (var item in Collection) { col = _fromCol; if (item is string || item is decimal || item is DateTime || item.GetType().IsPrimitive) { _worksheet.Cells[row, col++].Value = item; } else { foreach (var t in Members) { if (t is PropertyInfo) { _worksheet.Cells[row, col++].Value = ((PropertyInfo)t).GetValue(item, null); } else if (t is FieldInfo) { _worksheet.Cells[row, col++].Value = ((FieldInfo)t).GetValue(item); } else if (t is MethodInfo) { _worksheet.Cells[row, col++].Value = ((MethodInfo)t).Invoke(item, null); } } } row++; } } if (_fromRow == row-1 && PrintHeaders) { row++; } var r = _worksheet.Cells[_fromRow, _fromCol, row - 1, Members.Length==0 ? col : col - 1]; if (TableStyle != TableStyles.None) { var tbl = _worksheet.Tables.Add(r, ""); tbl.ShowHeader = PrintHeaders; tbl.TableStyle = TableStyle; } return r; } #endregion #region LoadFromText /// /// Loads a CSV text into a range starting from the top left cell. /// Default settings is Comma separation /// /// The Text /// The range containing the data public ExcelRangeBase LoadFromText(string Text) { return LoadFromText(Text, new ExcelTextFormat()); } /// /// Loads a CSV text into a range starting from the top left cell. /// /// The Text /// Information how to load the text /// The range containing the data public ExcelRangeBase LoadFromText(string Text, ExcelTextFormat Format) { if (string.IsNullOrEmpty(Text)) { var r = _worksheet.Cells[_fromRow, _fromCol]; r.Value = ""; return r; } if (Format == null) Format = new ExcelTextFormat(); string[] lines = Regex.Split(Text, Format.EOL); int row = _fromRow; int col = _fromCol; int maxCol = col; int lineNo = 1; foreach (string line in lines) { if (lineNo > Format.SkipLinesBeginning && lineNo <= lines.Length - Format.SkipLinesEnd) { col = _fromCol; string v = ""; bool isText = false, isQualifier = false; int QCount = 0; foreach (char c in line) { if (Format.TextQualifier != 0 && c == Format.TextQualifier) { if (!isText && v != "") { throw (new Exception(string.Format("Invalid Text Qualifier in line : {0}", line))); } isQualifier = !isQualifier; QCount += 1; isText = true; } else { if (QCount > 1 && !string.IsNullOrEmpty(v)) { v += new string(Format.TextQualifier, QCount / 2); } else if(QCount>2 && string.IsNullOrEmpty(v)) { v += new string(Format.TextQualifier, (QCount-1) / 2); } if (isQualifier) { v += c; } else { if (c == Format.Delimiter) { _worksheet.SetValue(row, col, ConvertData(Format, v, col - _fromCol, isText)); v = ""; isText = false; col++; } else { if (QCount % 2 == 1) { throw (new Exception(string.Format("Text delimiter is not closed in line : {0}", line))); } v += c; } } QCount = 0; } } if (QCount > 1) { v += new string(Format.TextQualifier, QCount / 2); } _worksheet._values.SetValue(row, col, ConvertData(Format, v, col - _fromCol, isText)); if (col > maxCol) maxCol = col; row++; } lineNo++; } return _worksheet.Cells[_fromRow, _fromCol, row - 1, maxCol]; } /// /// Loads a CSV text into a range starting from the top left cell. /// /// The Text /// Information how to load the text /// Create a table with this style /// Use the first row as header /// public ExcelRangeBase LoadFromText(string Text, ExcelTextFormat Format, TableStyles TableStyle, bool FirstRowIsHeader) { var r = LoadFromText(Text, Format); var tbl = _worksheet.Tables.Add(r, ""); tbl.ShowHeader = FirstRowIsHeader; tbl.TableStyle = TableStyle; return r; } /// /// Loads a CSV file into a range starting from the top left cell. /// /// The Textfile /// public ExcelRangeBase LoadFromText(FileInfo TextFile) { return LoadFromText(File.ReadAllText(TextFile.FullName, Encoding.ASCII)); } /// /// Loads a CSV file into a range starting from the top left cell. /// /// The Textfile /// Information how to load the text /// public ExcelRangeBase LoadFromText(FileInfo TextFile, ExcelTextFormat Format) { return LoadFromText(File.ReadAllText(TextFile.FullName, Format.Encoding), Format); } /// /// Loads a CSV file into a range starting from the top left cell. /// /// The Textfile /// Information how to load the text /// Create a table with this style /// Use the first row as header /// public ExcelRangeBase LoadFromText(FileInfo TextFile, ExcelTextFormat Format, TableStyles TableStyle, bool FirstRowIsHeader) { return LoadFromText(File.ReadAllText(TextFile.FullName, Format.Encoding), Format, TableStyle, FirstRowIsHeader); } #endregion #region GetValue /// /// Get the strongly typed value of the cell. /// /// The type /// The value. If the value can't be converted to the specified type, the default value will be returned public T GetValue() { return _worksheet.GetTypedValue(Value); } #endregion /// /// Get a range with an offset from the top left cell. /// The new range has the same dimensions as the current range /// /// Row Offset /// Column Offset /// 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 ExcelRangeBase(_worksheet, address); } /// /// Get a range with an offset from the top left cell. /// /// Row Offset /// Column Offset /// Number of rows. Minimum 1 /// Number of colums. Minimum 1 /// public ExcelRangeBase Offset(int RowOffset, int ColumnOffset, int NumberOfRows, int NumberOfColumns) { if (NumberOfRows < 1 || NumberOfColumns < 1) { throw (new Exception("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 ExcelRangeBase(_worksheet, address); } /// /// Adds a new comment for the range. /// If this range contains more than one cell, the top left comment is returned by the method. /// /// /// /// A reference comment of the top left cell public ExcelComment AddComment(string Text, string Author) { //Check if any comments exists in the range and throw an exception _changePropMethod(Exists_Comment, null); //Create the comments _changePropMethod(Set_Comment, new string[] { Text, Author }); return _worksheet.Comments[new ExcelCellAddress(_fromRow, _fromCol)]; } ///// ///// Copies the range of cells to an other range ///// ///// The start cell where the range will be copied. public void Copy(ExcelRangeBase Destination) { bool sameWorkbook = Destination._worksheet.Workbook == _worksheet.Workbook; ExcelStyles sourceStyles = _worksheet.Workbook.Styles, styles = Destination._worksheet.Workbook.Styles; Dictionary styleCashe = new Dictionary(); //Delete 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(_worksheet._values, _fromRow, _fromCol, _toRow, _toCol); var copiedValue = new List(); 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(_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(); //Merged cells var csem = new CellsStoreEnumerator(_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(this.Collide(adr)==eAddressCollition.Inside) { copiedMergedCells.Add(csem.Value, new ExcelAddress( Destination._fromRow + (adr.Start.Row - _fromRow), Destination._fromCol + (adr.Start.Column - _fromCol), Destination._toRow + (adr.End.Row - _fromRow), Destination._toCol + (adr.End.Column - _fromCol))); } else { //Partial merge of the address ignore. copiedMergedCells.Add(csem.Value, null); } } } Destination._worksheet.MergedCells.Delete(new ExcelAddressBase(Destination._fromRow, Destination._fromCol, Destination._fromRow+toRow, Destination._fromCol+toCol)); 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); } } //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 mergedAddresses = new List(); // 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 mergedAddresses) //{ // foreach (var address in mergedAddresses) // { // if (address.Collide(new ExcelAddressBase(cell.CellAddress)) == eAddressCollition.Inside) // { // return true; // } // } // return false; //} //private bool IsMerged(Dictionary 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; } /// /// Clear all cells /// public void Clear() { Delete(this, false); } /// /// Creates an array-formula. /// /// The formula public void CreateArrayFormula(string ArrayFormula) { if (Addresses != null) { throw (new Exception("An Arrayformula can not have more than one address")); } Set_SharedFormula(ArrayFormula, this, true); } //private void Delete(ExcelAddressBase Range) //{ // Delete(Range, true); //} internal void Delete(ExcelAddressBase Range, bool shift) { //DeleteCheckMergedCells(Range); _worksheet.MergedCells.Delete(Range); //First find the start cell var rows=Range._toRow-Range._fromRow+1; var cols=Range._toCol - Range._fromCol+1; _worksheet._values.Delete(Range._fromRow, Range._fromCol, rows, cols, shift); _worksheet._types.Delete(Range._fromRow, Range._fromCol, rows, cols, shift); _worksheet._styles.Delete(Range._fromRow, Range._fromCol, rows, cols, shift); _worksheet._formulas.Delete(Range._fromRow, Range._fromCol, rows, cols, shift); _worksheet._hyperLinks.Delete(Range._fromRow, Range._fromCol, rows, cols, shift); _worksheet._flags.Delete(Range._fromRow, Range._fromCol, rows, cols, shift); _worksheet._commentsStore.Delete(Range._fromRow, Range._fromCol, rows, cols, shift); //if(shift) //{ // _worksheet.AdjustFormulasRow(Range._fromRow, rows); //} //Delete multi addresses as well if (Addresses != null) { foreach (var sub in Addresses) { Delete(sub, shift); } } } private void DeleteCheckMergedCells(ExcelAddressBase Range) { var removeItems = new List(); foreach (var addr in Worksheet.MergedCells) { var addrCol = Range.Collide(new ExcelAddress(Range.WorkSheet, addr)); if (addrCol != eAddressCollition.No) { if (addrCol == eAddressCollition.Inside) { removeItems.Add(addr); } else { throw (new InvalidOperationException("Can't remove/overwrite a part of cells that are merged")); } } } foreach (var item in removeItems) { Worksheet.MergedCells.Remove(item); } } #endregion #region IDisposable Members public void Dispose() { //_worksheet = null; } #endregion #region "Enumerator" //int _index; //ulong _toCellId; //int _enumAddressIx; CellsStoreEnumerator cellEnum; public IEnumerator GetEnumerator() { return new ExcelRangeBaseEnumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return new ExcelRangeBaseEnumerator(this); } /// /// The current range when enumerating /// public class ExcelRangeBaseEnumerator : IEnumerator { private CellsStoreEnumerator _cellEnum; private int _enumAddressIx = -1; private ExcelRangeBase _range; private ExcelRangeBase _current; /// /// The current range when enumerating /// public ExcelRangeBase Current { get { return _current; } } /// /// The current range when enumerating /// object IEnumerator.Current { get { return _current; } } public ExcelRangeBaseEnumerator(ExcelRangeBase range) { this._range = range; Reset(); } public bool MoveNext() { if (_cellEnum.Next()) { _current._fromCol = _cellEnum.Column; _current._fromRow = _cellEnum.Row; _current._toCol = _cellEnum.Column; _current._toRow = _cellEnum.Row; _current.Address = GetAddress(_current._fromRow, _current._fromCol); return true; } else if (_range._addresses != null) { _enumAddressIx++; if (_enumAddressIx < _range._addresses.Count) { _cellEnum = new CellsStoreEnumerator(_range._worksheet._values, _range._addresses[_enumAddressIx]._fromRow, _range._addresses[_enumAddressIx]._fromCol, _range._addresses[_enumAddressIx]._toRow, _range._addresses[_enumAddressIx]._toCol); return MoveNext(); } else { return false; } } return false; } public void Reset() { _enumAddressIx = -1; _cellEnum = new CellsStoreEnumerator(_range._worksheet._values, _range._fromRow, _range._fromCol, _range._toRow, _range._toCol); _current = new ExcelRangeBase(_range._worksheet, ExcelAddressBase.GetAddress(_cellEnum.Row, _cellEnum.Column)); } public void Dispose() { if (_cellEnum != null) { _cellEnum.Dispose(); _cellEnum = null; } } } //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--; //} #endregion } }