diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 984600d..5b1b779 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,5 @@ +## Read and complete the full issue template + **Do you want to request a *feature* or report a *bug*?** - [x] Bug - [ ] Feature @@ -8,20 +10,26 @@ **What is the current behavior?** -**If the current behavior is a bug, please provide the steps to reproduce and -if possible a minimal demo of the problem with a sample spreadsheet.** +Complete this. **What is the expected behavior or new feature?** +Complete this. + **Did this work in previous versions of our tool? Which versions?** -- [ ] I attached a sample spreadsheet. (You can drag files on to this issue) +Yes/No/v0.XX + +## Reproducibility +**This is an important section. Read it carefully. Failure to do so will cause a 'RTFM' comment.** + +Without a code sample, it is unlikely that your issue will get attention. Don't be lazy. Do the effort and assist the developers to reproduce your problem. Code samples should be [minimal complete and verifiable](https://stackoverflow.com/help/mcve). Sample spreadsheets should be attached whenever applicable. Remove sensitive information. **Code to reproduce problem:** ```c# public void Main() { - // Where possible, post full code to reproduce your issue that adheres to: + // Code standards: // - Fully runnable. I should be able to copy and paste this code into a // console application and run it without having to edit it much. // - Declare all your variables (this follows from the previous point) @@ -30,3 +38,4 @@ // post your full application. } ``` +- [ ] I attached a sample spreadsheet. (You can drag files on to this issue) diff --git a/ClosedXML/Excel/CalcEngine/CalcEngine.cs b/ClosedXML/Excel/CalcEngine/CalcEngine.cs index 8cc752e..c1a1aa1 100644 --- a/ClosedXML/Excel/CalcEngine/CalcEngine.cs +++ b/ClosedXML/Excel/CalcEngine/CalcEngine.cs @@ -253,6 +253,18 @@ #region ** token/keyword tables + private static readonly IDictionary ErrorMap = new Dictionary() + { + ["#REF!"] = ErrorExpression.ExpressionErrorType.CellReference, + ["#VALUE!"] = ErrorExpression.ExpressionErrorType.CellValue, + ["#DIV/0!"] = ErrorExpression.ExpressionErrorType.DivisionByZero, + ["#NAME?"] = ErrorExpression.ExpressionErrorType.NameNotRecognized, + ["#N/A"] = ErrorExpression.ExpressionErrorType.NoValueAvailable, + ["#NULL!"] = ErrorExpression.ExpressionErrorType.NullValue, + ["#NUM!"] = ErrorExpression.ExpressionErrorType.NumberInvalid + }; + + // build/get static token table private Dictionary GetSymbolTable() { @@ -463,6 +475,10 @@ } break; + + case TKTYPE.ERROR: + x = new ErrorExpression((ErrorExpression.ExpressionErrorType)_token.Value); + break; } // make sure we got something... @@ -663,26 +679,12 @@ return; } - // parse dates (review) - if (c == '#') + // parse #REF! (and other errors) in formula + if (c == '#' && ErrorMap.Any(pair => _len > _ptr+pair.Key.Length && _expr.Substring(_ptr, pair.Key.Length).Equals(pair.Key, StringComparison.OrdinalIgnoreCase))) { - // look for end # - for (i = 1; i + _ptr < _len; i++) - { - c = _expr[_ptr + i]; - if (c == '#') break; - } - - // check that we got the end of the date - if (c != '#') - { - Throw("Can't find final date delimiter ('#')."); - } - - // end of date - var lit = _expr.Substring(_ptr + 1, i - 1); - _ptr += i + 1; - _token = new Token(DateTime.Parse(lit, _ci), TKID.ATOM, TKTYPE.LITERAL); + var errorPair = ErrorMap.Single(pair => _len > _ptr + pair.Key.Length && _expr.Substring(_ptr, pair.Key.Length).Equals(pair.Key, StringComparison.OrdinalIgnoreCase)); + _ptr += errorPair.Key.Length; + _token = new Token(errorPair.Value, TKID.ATOM, TKTYPE.ERROR); return; } diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/CalcEngineException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/CalcEngineException.cs new file mode 100644 index 0000000..962b1c6 --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/CalcEngineException.cs @@ -0,0 +1,18 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + internal abstract class CalcEngineException : ArgumentException + { + protected CalcEngineException() + : base() + { } + protected CalcEngineException(string message) + : base(message) + { } + + protected CalcEngineException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/CellReferenceException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/CellReferenceException.cs new file mode 100644 index 0000000..f0fd88c --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/CellReferenceException.cs @@ -0,0 +1,27 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + /// + /// This error occurs when you delete a cell referred to in the + /// formula or if you paste cells over the ones referred to in the + /// formula. + /// Corresponds to the #REF! error in Excel + /// + /// + internal class CellReferenceException : CalcEngineException + { + public CellReferenceException() + : base() + { } + + public CellReferenceException(string message) + : base(message) + { } + + public CellReferenceException(string message, Exception innerException) + : base(message, innerException) + { } + + } +} diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/CellValueException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/CellValueException.cs new file mode 100644 index 0000000..0716353 --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/CellValueException.cs @@ -0,0 +1,26 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + /// + /// This error is most often the result of specifying a + /// mathematical operation with one or more cells that contain + /// text. + /// Corresponds to the #VALUE! error in Excel + /// + /// + internal class CellValueException : CalcEngineException + { + public CellValueException() + : base() + { } + + public CellValueException(string message) + : base(message) + { } + + public CellValueException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/DivisionByZeroException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/DivisionByZeroException.cs new file mode 100644 index 0000000..53e2ed1 --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/DivisionByZeroException.cs @@ -0,0 +1,26 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + /// + /// The division operation in your formula refers to a cell that + /// contains the value 0 or is blank. + /// Corresponds to the #DIV/0! error in Excel + /// + /// + internal class DivisionByZeroException : CalcEngineException + { + public DivisionByZeroException() + : base() + { } + + public DivisionByZeroException(string message) + : base(message) + { } + + public DivisionByZeroException(string message, Exception innerException) + : base(message, innerException) + { } + + } +} \ No newline at end of file diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/NameNotRecognizedException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/NameNotRecognizedException.cs new file mode 100644 index 0000000..0f51e56 --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/NameNotRecognizedException.cs @@ -0,0 +1,27 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + /// + /// This error value appears when you incorrectly type the range + /// name, refer to a deleted range name, or forget to put quotation + /// marks around a text string in a formula. + /// Corresponds to the #NAME? error in Excel + /// + /// + internal class NameNotRecognizedException : CalcEngineException + { + public NameNotRecognizedException() + : base() + { } + + public NameNotRecognizedException(string message) + : base(message) + { } + + public NameNotRecognizedException(string message, Exception innerException) + : base(message, innerException) + { } + + } +} diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/NoValueAvailableException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/NoValueAvailableException.cs new file mode 100644 index 0000000..0e97fe5 --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/NoValueAvailableException.cs @@ -0,0 +1,27 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + /// + /// Technically, this is not an error value but a special value + /// that you can manually enter into a cell to indicate that you + /// don’t yet have a necessary value. + /// Corresponds to the #N/A error in Excel. + /// + /// + internal class NoValueAvailableException : CalcEngineException + { + public NoValueAvailableException() + : base() + { } + + public NoValueAvailableException(string message) + : base(message) + { } + + public NoValueAvailableException(string message, Exception innerException) + : base(message, innerException) + { } + + } +} \ No newline at end of file diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/NullValueException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/NullValueException.cs new file mode 100644 index 0000000..d3153d7 --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/NullValueException.cs @@ -0,0 +1,27 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + /// + /// Because a space indicates an intersection, this error will + /// occur if you insert a space instead of a comma(the union operator) + /// between ranges used in function arguments. + /// Corresponds to the #NULL! error in Excel + /// + /// + internal class NullValueException : CalcEngineException + { + public NullValueException() + : base() + { } + + public NullValueException(string message) + : base(message) + { } + + public NullValueException(string message, Exception innerException) + : base(message, innerException) + { } + + } +} diff --git a/ClosedXML/Excel/CalcEngine/Exceptions/NumberException.cs b/ClosedXML/Excel/CalcEngine/Exceptions/NumberException.cs new file mode 100644 index 0000000..4ce87d0 --- /dev/null +++ b/ClosedXML/Excel/CalcEngine/Exceptions/NumberException.cs @@ -0,0 +1,27 @@ +using System; + +namespace ClosedXML.Excel.CalcEngine.Exceptions +{ + /// + /// This error can be caused by an invalid argument in an Excel + /// function or a formula that produces a number too large or too small + /// to be represented in the worksheet. + /// Corresponds to the #NUM! error in Excel + /// + /// + internal class NumberException : CalcEngineException + { + public NumberException() + : base() + { } + + public NumberException(string message) + : base(message) + { } + + public NumberException(string message, Exception innerException) + : base(message, innerException) + { } + + } +} diff --git a/ClosedXML/Excel/CalcEngine/Expression.cs b/ClosedXML/Excel/CalcEngine/Expression.cs index 9dfdf28..b9a29ca 100644 --- a/ClosedXML/Excel/CalcEngine/Expression.cs +++ b/ClosedXML/Excel/CalcEngine/Expression.cs @@ -1,3 +1,4 @@ +using ClosedXML.Excel.CalcEngine.Exceptions; using System; using System.Collections; using System.Collections.Generic; @@ -79,12 +80,18 @@ public static implicit operator string(Expression x) { + if (x is ErrorExpression) + (x as ErrorExpression).ThrowApplicableException(); + var v = x.Evaluate(); return v == null ? string.Empty : v.ToString(); } public static implicit operator double(Expression x) { + if (x is ErrorExpression) + (x as ErrorExpression).ThrowApplicableException(); + // evaluate var v = x.Evaluate(); @@ -119,6 +126,9 @@ public static implicit operator bool(Expression x) { + if (x is ErrorExpression) + (x as ErrorExpression).ThrowApplicableException(); + // evaluate var v = x.Evaluate(); @@ -146,6 +156,9 @@ public static implicit operator DateTime(Expression x) { + if (x is ErrorExpression) + (x as ErrorExpression).ThrowApplicableException(); + // evaluate var v = x.Evaluate(); @@ -490,6 +503,52 @@ } } + internal class ErrorExpression : Expression + { + internal enum ExpressionErrorType + { + CellReference, + CellValue, + DivisionByZero, + NameNotRecognized, + NoValueAvailable, + NullValue, + NumberInvalid + } + + internal ErrorExpression(ExpressionErrorType eet) + : base(new Token(eet, TKID.ATOM, TKTYPE.ERROR)) + { } + + public override object Evaluate() + { + return this._token.Value; + } + + public void ThrowApplicableException() + { + var eet = (ExpressionErrorType)_token.Value; + switch (eet) + { + // TODO: include last token in exception message + case ExpressionErrorType.CellReference: + throw new CellReferenceException(); + case ExpressionErrorType.CellValue: + throw new CellValueException(); + case ExpressionErrorType.DivisionByZero: + throw new DivisionByZeroException(); + case ExpressionErrorType.NameNotRecognized: + throw new NameNotRecognizedException(); + case ExpressionErrorType.NoValueAvailable: + throw new NoValueAvailableException(); + case ExpressionErrorType.NullValue: + throw new NullValueException(); + case ExpressionErrorType.NumberInvalid: + throw new NumberException(); + } + } + } + /// /// Interface supported by external objects that have to return a value /// other than themselves (e.g. a cell range object should return the diff --git a/ClosedXML/Excel/CalcEngine/Functions/Information.cs b/ClosedXML/Excel/CalcEngine/Functions/Information.cs index 6df1a1e..6b19dfb 100644 --- a/ClosedXML/Excel/CalcEngine/Functions/Information.cs +++ b/ClosedXML/Excel/CalcEngine/Functions/Information.cs @@ -1,3 +1,4 @@ +using ClosedXML.Excel.CalcEngine.Exceptions; using System; using System.Collections.Generic; using System.Globalization; @@ -9,27 +10,42 @@ public static void Register(CalcEngine ce) { //TODO: Add documentation - ce.RegisterFunction("ERRORTYPE",1,ErrorType); - ce.RegisterFunction("ISBLANK", 1,int.MaxValue, IsBlank); - ce.RegisterFunction("ISERR",1, int.MaxValue, IsErr); - ce.RegisterFunction("ISERROR",1, int.MaxValue, IsError); - ce.RegisterFunction("ISEVEN",1, IsEven); - ce.RegisterFunction("ISLOGICAL",1,int.MaxValue,IsLogical); - ce.RegisterFunction("ISNA",1, int.MaxValue, IsNa); - ce.RegisterFunction("ISNONTEXT",1, int.MaxValue, IsNonText); - ce.RegisterFunction("ISNUMBER",1, int.MaxValue, IsNumber); - ce.RegisterFunction("ISODD",1,IsOdd); - ce.RegisterFunction("ISREF",1, int.MaxValue, IsRef); + ce.RegisterFunction("ERRORTYPE", 1, ErrorType); + ce.RegisterFunction("ISBLANK", 1, int.MaxValue, IsBlank); + ce.RegisterFunction("ISERR", 1, int.MaxValue, IsErr); + ce.RegisterFunction("ISERROR", 1, int.MaxValue, IsError); + ce.RegisterFunction("ISEVEN", 1, IsEven); + ce.RegisterFunction("ISLOGICAL", 1, int.MaxValue, IsLogical); + ce.RegisterFunction("ISNA", 1, int.MaxValue, IsNa); + ce.RegisterFunction("ISNONTEXT", 1, int.MaxValue, IsNonText); + ce.RegisterFunction("ISNUMBER", 1, int.MaxValue, IsNumber); + ce.RegisterFunction("ISODD", 1, IsOdd); + ce.RegisterFunction("ISREF", 1, int.MaxValue, IsRef); ce.RegisterFunction("ISTEXT", 1, int.MaxValue, IsText); - ce.RegisterFunction("N",1,N); - ce.RegisterFunction("NA",0,NA); - ce.RegisterFunction("TYPE",1,Type); + ce.RegisterFunction("N", 1, N); + ce.RegisterFunction("NA", 0, NA); + ce.RegisterFunction("TYPE", 1, Type); } + static IDictionary errorTypes = new Dictionary() + { + [ErrorExpression.ExpressionErrorType.NullValue] = 1, + [ErrorExpression.ExpressionErrorType.DivisionByZero] = 2, + [ErrorExpression.ExpressionErrorType.CellValue] = 3, + [ErrorExpression.ExpressionErrorType.CellReference] = 4, + [ErrorExpression.ExpressionErrorType.NameNotRecognized] = 5, + [ErrorExpression.ExpressionErrorType.NumberInvalid] = 6, + [ErrorExpression.ExpressionErrorType.NoValueAvailable] = 7 + }; + static object ErrorType(List p) { - //TODO: Write Code - throw new NotSupportedException();; + var v = p[0].Evaluate(); + + if (v is ErrorExpression.ExpressionErrorType) + return errorTypes[(ErrorExpression.ExpressionErrorType)v]; + else + throw new NoValueAvailableException(); } static object IsBlank(List p) @@ -46,17 +62,19 @@ return isBlank; } - //TODO: Support for Error Values static object IsErr(List p) { - //TODO: Write Code - throw new NotSupportedException(); + var v = p[0].Evaluate(); + + return v is ErrorExpression.ExpressionErrorType + && ((ErrorExpression.ExpressionErrorType)v) != ErrorExpression.ExpressionErrorType.NoValueAvailable; } - + static object IsError(List p) { - //TODO: Write Code - throw new NotSupportedException(); + var v = p[0].Evaluate(); + + return v is ErrorExpression.ExpressionErrorType; } static object IsEven(List p) @@ -74,7 +92,7 @@ { var v = p[0].Evaluate(); var isLogical = v is bool; - + if (isLogical && p.Count > 1) { var sublist = p.GetRange(1, p.Count); @@ -86,8 +104,10 @@ static object IsNa(List p) { - //TODO: Write Code - throw new NotSupportedException();; + var v = p[0].Evaluate(); + + return v is ErrorExpression.ExpressionErrorType + && ((ErrorExpression.ExpressionErrorType)v) == ErrorExpression.ExpressionErrorType.NoValueAvailable; } static object IsNonText(List p) @@ -110,16 +130,16 @@ try { var stringValue = (string) v; - double.Parse(stringValue.TrimEnd('%', ' '), NumberStyles.Any); - isNumber = true; + double dv; + return double.TryParse(stringValue.TrimEnd('%', ' '), NumberStyles.Any, null, out dv); } catch (Exception) { isNumber = false; } } - - if (isNumber && p.Count > 1) + + if (isNumber && p.Count > 1) { var sublist = p.GetRange(1, p.Count); isNumber = (bool)IsNumber(sublist); @@ -135,8 +155,13 @@ static object IsRef(List p) { - //TODO: Write Code - throw new NotSupportedException();; + var oe = p[0] as XObjectExpression; + if (oe == null) + return false; + + var crr = oe.Value as CellRangeReference; + + return crr != null; } static object IsText(List p) @@ -161,8 +186,7 @@ static object NA(List p) { - //TODO: Write Code - throw new NotSupportedException();; + return ErrorExpression.ExpressionErrorType.NoValueAvailable; } static object Type(List p) @@ -190,4 +214,4 @@ return null; } } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/CalcEngine/Functions/Lookup.cs b/ClosedXML/Excel/CalcEngine/Functions/Lookup.cs index 964e72b..801a195 100644 --- a/ClosedXML/Excel/CalcEngine/Functions/Lookup.cs +++ b/ClosedXML/Excel/CalcEngine/Functions/Lookup.cs @@ -1,3 +1,4 @@ +using ClosedXML.Excel.CalcEngine.Exceptions; using System; using System.Collections.Generic; using System.Linq; @@ -35,11 +36,11 @@ var table_array = p[1] as XObjectExpression; if (table_array == null) - throw new ArgumentException("table_array has to be a range"); + throw new NoValueAvailableException("table_array has to be a range"); var range_reference = table_array.Value as CellRangeReference; if (range_reference == null) - throw new ArgumentException("table_array has to be a range"); + throw new NoValueAvailableException("table_array has to be a range"); var range = range_reference.Range; @@ -49,10 +50,10 @@ || (bool)(p[3]); if (row_index_num < 1) - throw new ArgumentOutOfRangeException("Row index", "row_index_num has to be positive"); + throw new CellReferenceException("Row index has to be positive"); if (row_index_num > range.RowCount()) - throw new ArgumentOutOfRangeException("Row index", "row_index_num has to be positive"); + throw new CellReferenceException("Row index has to be positive"); IXLRangeColumn matching_column; matching_column = range.FindColumn(c => !c.Cell(1).IsEmpty() && new Expression(c.Cell(1).Value).CompareTo(lookup_value) == 0); @@ -72,7 +73,7 @@ } if (matching_column == null) - throw new ArgumentException("No matches found."); + throw new NoValueAvailableException("No matches found."); return matching_column .Cell(row_index_num) @@ -85,11 +86,11 @@ var table_array = p[1] as XObjectExpression; if (table_array == null) - throw new ArgumentException("table_array has to be a range"); + throw new NoValueAvailableException("table_array has to be a range"); var range_reference = table_array.Value as CellRangeReference; if (range_reference == null) - throw new ArgumentException("table_array has to be a range"); + throw new NoValueAvailableException("table_array has to be a range"); var range = range_reference.Range; @@ -99,13 +100,20 @@ || (bool)(p[3]); if (col_index_num < 1) - throw new ArgumentOutOfRangeException("Column index", "col_index_num has to be positive"); + throw new CellReferenceException("Column index has to be positive"); if (col_index_num > range.ColumnCount()) - throw new ArgumentOutOfRangeException("Column index", "col_index_num must be smaller or equal to the number of columns in the table array"); + throw new CellReferenceException("Colum index must be smaller or equal to the number of columns in the table array"); IXLRangeRow matching_row; - matching_row = range.FindRow(r => !r.Cell(1).IsEmpty() && new Expression(r.Cell(1).Value).CompareTo(lookup_value) == 0); + try + { + matching_row = range.FindRow(r => !r.Cell(1).IsEmpty() && new Expression(r.Cell(1).Value).CompareTo(lookup_value) == 0); + } + catch (Exception ex) + { + throw new NoValueAvailableException("No matches found", ex); + } if (range_lookup && matching_row == null) { var first_row = range.FirstRow().RowNumber(); @@ -122,7 +130,7 @@ } if (matching_row == null) - throw new ArgumentException("No matches found."); + throw new NoValueAvailableException("No matches found."); return matching_row .Cell(col_index_num) diff --git a/ClosedXML/Excel/CalcEngine/Functions/Text.cs b/ClosedXML/Excel/CalcEngine/Functions/Text.cs index 6046455..8ff1da3 100644 --- a/ClosedXML/Excel/CalcEngine/Functions/Text.cs +++ b/ClosedXML/Excel/CalcEngine/Functions/Text.cs @@ -1,3 +1,4 @@ +using ClosedXML.Excel.CalcEngine.Exceptions; using System; using System.Collections.Generic; using System.Globalization; @@ -44,7 +45,9 @@ private static object _Char(List p) { var i = (int)p[0]; - if (i < 1 || i > 255) throw new IndexOutOfRangeException(); + if (i < 1 || i > 255) + throw new CellValueException(string.Format("The number {0} is out of the required range (1 to 255)", i)); + var c = (char)i; return c.ToString(); } diff --git a/ClosedXML/Excel/CalcEngine/Token.cs b/ClosedXML/Excel/CalcEngine/Token.cs index 67351df..4766a51 100644 --- a/ClosedXML/Excel/CalcEngine/Token.cs +++ b/ClosedXML/Excel/CalcEngine/Token.cs @@ -1,12 +1,13 @@ namespace ClosedXML.Excel.CalcEngine { /// - /// Represents a node in the expression tree. + /// Represents a node in the expression tree. /// internal class Token - { + { // ** fields - public TKID ID; + public TKID ID; + public TKTYPE Type; public object Value; @@ -15,22 +16,25 @@ { Value = value; ID = id; - Type = type; - } + Type = type; + } } + /// /// Token types (used when building expressions, sequence defines operator priority) /// internal enum TKTYPE { - COMPARE, // < > = <= >= - ADDSUB, // + - - MULDIV, // * / - POWER, // ^ - GROUP, // ( ) , . - LITERAL, // 123.32, "Hello", etc. - IDENTIFIER // functions, external objects, bindings + COMPARE, // < > = <= >= + ADDSUB, // + - + MULDIV, // * / + POWER, // ^ + GROUP, // ( ) , . + LITERAL, // 123.32, "Hello", etc. + IDENTIFIER, // functions, external objects, bindings + ERROR // e.g. #REF! } + /// /// Token ID (used when evaluating expressions) /// diff --git a/ClosedXML/Excel/Cells/IXLCell.cs b/ClosedXML/Excel/Cells/IXLCell.cs index 0f6f6b5..fc3724e 100644 --- a/ClosedXML/Excel/Cells/IXLCell.cs +++ b/ClosedXML/Excel/Cells/IXLCell.cs @@ -7,6 +7,8 @@ { public enum XLCellValues { Text, Number, Boolean, DateTime, TimeSpan } + public enum XLTableCellType { None, Header, Data, Total } + public enum XLClearOptions { ContentsAndFormats, @@ -30,7 +32,7 @@ /// Gets this cell's address, relative to the worksheet. /// The cell's address. - IXLAddress Address { get; } + IXLAddress Address { get; } /// /// Gets or sets the type of this cell's data. @@ -115,8 +117,6 @@ Boolean TryGetValue(out T value); - - Boolean HasHyperlink { get; } /// @@ -152,7 +152,6 @@ /// IXLRange AsRange(); - /// /// Gets or sets the cell's style. /// @@ -173,14 +172,6 @@ IXLRange InsertData(IEnumerable data); /// - - /// Inserts the data of a data table. - /// - /// The data table. - /// The range occupied by the inserted data - IXLRange InsertData(DataTable dataTable); - - /// /// Inserts the IEnumerable data elements and returns the range it occupies. /// /// The IEnumerable data. @@ -189,6 +180,13 @@ IXLRange InsertData(IEnumerable data, Boolean tranpose); /// + /// Inserts the data of a data table. + /// + /// The data table. + /// The range occupied by the inserted data + IXLRange InsertData(DataTable dataTable); + + /// /// Inserts the IEnumerable data elements as a table and returns it. /// The new table will receive a generic name: Table# /// @@ -224,7 +222,6 @@ /// IXLTable InsertTable(IEnumerable data, String tableName, Boolean createTable); - /// /// Inserts the DataTable data elements as a table and returns it. /// The new table will receive a generic name: Table# @@ -261,18 +258,22 @@ /// IXLTable InsertTable(DataTable data, String tableName, Boolean createTable); + XLTableCellType TableCellType(); XLHyperlink Hyperlink { get; set; } IXLWorksheet Worksheet { get; } IXLDataValidation DataValidation { get; } IXLDataValidation NewDataValidation { get; } + IXLDataValidation SetDataValidation(); - IXLCells InsertCellsAbove(int numberOfRows); + IXLCells InsertCellsBelow(int numberOfRows); + IXLCells InsertCellsAfter(int numberOfColumns); + IXLCells InsertCellsBefore(int numberOfColumns); /// @@ -301,8 +302,11 @@ IXLCell AddToNamed(String rangeName, XLScope scope, String comment); IXLCell CopyFrom(IXLCell otherCell); + IXLCell CopyFrom(String otherCell); + IXLCell CopyTo(IXLCell target); + IXLCell CopyTo(String target); String ValueCached { get; } @@ -313,19 +317,29 @@ Boolean HasComment { get; } Boolean IsMerged(); + Boolean IsEmpty(); + Boolean IsEmpty(Boolean includeFormats); IXLCell CellAbove(); + IXLCell CellAbove(Int32 step); + IXLCell CellBelow(); + IXLCell CellBelow(Int32 step); + IXLCell CellLeft(); + IXLCell CellLeft(Int32 step); + IXLCell CellRight(); + IXLCell CellRight(Int32 step); IXLColumn WorksheetColumn(); + IXLRow WorksheetRow(); Boolean HasDataValidation { get; } @@ -335,6 +349,7 @@ void Select(); Boolean Active { get; set; } + IXLCell SetActive(Boolean value = true); Boolean HasFormula { get; } diff --git a/ClosedXML/Excel/Cells/XLCell.cs b/ClosedXML/Excel/Cells/XLCell.cs index 23bbba7..508c9de 100644 --- a/ClosedXML/Excel/Cells/XLCell.cs +++ b/ClosedXML/Excel/Cells/XLCell.cs @@ -15,6 +15,7 @@ using Attributes; using ClosedXML.Extensions; + [DebuggerDisplay("{Address}")] internal class XLCell : IXLCell, IXLStylized { public static readonly DateTime BaseDate = new DateTime(1899, 12, 30); @@ -73,49 +74,7 @@ internal XLCellValues _dataType; private XLHyperlink _hyperlink; private XLRichText _richText; - - #endregion Fields - - #region Constructor - - private Int32 _styleCacheId; - - public XLCell(XLWorksheet worksheet, XLAddress address, Int32 styleId) - { - Address = address; - ShareString = true; - _worksheet = worksheet; - SetStyle(styleId); - } - - private IXLStyle GetStyleForRead() - { - return Worksheet.Workbook.GetStyleById(GetStyleId()); - } - - public Int32 GetStyleId() - { - if (StyleChanged) - SetStyle(Style); - - return _styleCacheId; - } - - private void SetStyle(IXLStyle styleToUse) - { - _styleCacheId = Worksheet.Workbook.GetStyleId(styleToUse); - _style = null; - StyleChanged = false; - } - - private void SetStyle(Int32 styleId) - { - _styleCacheId = styleId; - _style = null; - StyleChanged = false; - } - - #endregion Constructor + private Int32? _styleCacheId; public bool SettingHyperlink; public int SharedStringId; @@ -123,6 +82,25 @@ private string _formulaR1C1; private IXLStyle _style; + #endregion Fields + + #region Constructor + + public XLCell(XLWorksheet worksheet, XLAddress address, Int32 styleId) + : this(worksheet, address) + { + SetStyle(styleId); + } + + public XLCell(XLWorksheet worksheet, XLAddress address) + { + Address = address; + ShareString = true; + _worksheet = worksheet; + } + + #endregion Constructor + public XLWorksheet Worksheet { get { return _worksheet; } @@ -221,11 +199,23 @@ public IXLCell SetValue(T value) { + return SetValue(value, true); + } + + internal IXLCell SetValue(T value, bool setTableHeader) + { if (value == null) return this.Clear(XLClearOptions.Contents); FormulaA1 = String.Empty; _richText = null; + + if (setTableHeader) + { + if (SetTableHeaderValue(value)) return this; + if (SetTableTotalsRowLabel(value)) return this; + } + var style = GetStyleForRead(); if (value is String || value is char) { @@ -399,8 +389,7 @@ var retValEnumerable = retVal as IEnumerable; if (retValEnumerable != null && !(retVal is String)) - foreach (var v in retValEnumerable) - return v; + return retValEnumerable.Cast().First(); return retVal; } @@ -439,9 +428,9 @@ { FormulaA1 = String.Empty; - if (value as XLCells != null) throw new ArgumentException("Cannot assign IXLCells object to the cell value."); + if (value is XLCells) throw new ArgumentException("Cannot assign IXLCells object to the cell value."); - if (SetTableHeader(value)) return; + if (SetTableHeaderValue(value)) return; if (SetRangeRows(value)) return; @@ -456,7 +445,7 @@ if (!SetRichText(value)) SetValue(value); - if (_cellValue.Length > 32767) throw new ArgumentException("Cells can only hold 32,767 characters."); + if (_cellValue.Length > 32767) throw new ArgumentException("Cells can hold only 32,767 characters."); } } @@ -477,6 +466,9 @@ public IXLTable InsertTable(IEnumerable data, string tableName, bool createTable) { + if (createTable && this.Worksheet.Tables.Any(t => t.Contains(this))) + throw new InvalidOperationException(String.Format("This cell '{0}' is already part of a table.", this.Address.ToString())); + if (data != null && !(data is String)) { var ro = Address.RowNumber + 1; @@ -506,12 +498,12 @@ if (String.IsNullOrWhiteSpace(fieldName)) fieldName = itemType.Name; - SetValue(fieldName, fRo, co); + _worksheet.SetValue(fieldName, fRo, co); hasTitles = true; co = Address.ColumnNumber; } - SetValue(o, ro, co); + _worksheet.SetValue(o, ro, co); co++; if (co > maxCo) @@ -522,7 +514,7 @@ } else { - const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; var memberCache = new Dictionary>(); var accessorCache = new Dictionary(); IEnumerable members = null; @@ -568,7 +560,7 @@ { foreach (var item in (m as Array)) { - SetValue(item, ro, co); + _worksheet.SetValue(item, ro, co); co++; } } @@ -585,7 +577,7 @@ ? column.ColumnName : column.Caption) { - SetValue(fieldName, fRo, co); + _worksheet.SetValue(fieldName, fRo, co); co++; } @@ -595,7 +587,7 @@ foreach (var item in row.ItemArray) { - SetValue(item, ro, co); + _worksheet.SetValue(item, ro, co); co++; } } @@ -611,7 +603,7 @@ { for (var i = 0; i < fieldCount; i++) { - SetValue(record.GetName(i), fRo, co); + _worksheet.SetValue(record.GetName(i), fRo, co); co++; } @@ -621,7 +613,7 @@ for (var i = 0; i < fieldCount; i++) { - SetValue(record[i], ro, co); + _worksheet.SetValue(record[i], ro, co); co++; } } @@ -631,13 +623,13 @@ { foreach (var mi in members) { - if ((mi as IEnumerable) == null) + if (!(mi is IEnumerable)) { var fieldName = XLColumnAttribute.GetHeader(mi); if (String.IsNullOrWhiteSpace(fieldName)) fieldName = mi.Name; - SetValue(fieldName, fRo, co); + _worksheet.SetValue(fieldName, fRo, co); } co++; @@ -649,7 +641,13 @@ foreach (var mi in members) { - SetValue(accessor[m, mi.Name], ro, co); + if (mi.MemberType == MemberTypes.Property && (mi as PropertyInfo).GetGetMethod().IsStatic) + _worksheet.SetValue((mi as PropertyInfo).GetValue(null, null), ro, co); + else if (mi.MemberType == MemberTypes.Field && (mi as FieldInfo).IsStatic) + _worksheet.SetValue((mi as FieldInfo).GetValue(null), ro, co); + else + _worksheet.SetValue(accessor[m, mi.Name], ro, co); + co++; } } @@ -695,14 +693,16 @@ { if (data == null) return null; - if (data.Rows.Count > 0) return InsertTable(data.Rows.Cast(), tableName, createTable); + if (createTable && this.Worksheet.Tables.Any(t => t.Contains(this))) + throw new InvalidOperationException(String.Format("This cell '{0}' is already part of a table.", this.Address.ToString())); + if (data.Rows.Cast().Any()) return InsertTable(data.Rows.Cast(), tableName, createTable); var ro = Address.RowNumber; var co = Address.ColumnNumber; foreach (DataColumn col in data.Columns) { - SetValue(col.ColumnName, ro, co); + _worksheet.SetValue(col.ColumnName, ro, co); co++; } @@ -718,6 +718,17 @@ return tableName == null ? range.AsTable() : range.AsTable(tableName); } + public XLTableCellType TableCellType() + { + var table = this.Worksheet.Tables.FirstOrDefault(t => t.AsRange().Contains(this)); + if (table == null) return XLTableCellType.None; + + if (table.ShowHeaderRow && table.HeadersRow().RowNumber().Equals(this.Address.RowNumber)) return XLTableCellType.Header; + if (table.ShowTotalsRow && table.TotalsRow().RowNumber().Equals(this.Address.RowNumber)) return XLTableCellType.Total; + + return XLTableCellType.Data; + } + public IXLRange InsertData(IEnumerable data) { return InsertData(data, false); @@ -735,7 +746,7 @@ var isDataTable = false; var isDataReader = false; - const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; var memberCache = new Dictionary>(); var accessorCache = new Dictionary(); IEnumerable members = null; @@ -744,31 +755,15 @@ foreach (var m in data) { var itemType = m.GetType(); - if (!memberCache.ContainsKey(itemType)) - { - var _accessor = TypeAccessor.Create(itemType); - - var _members = itemType.GetFields(bindingFlags).Cast() - .Concat(itemType.GetProperties(bindingFlags)) - .Where(mi => !XLColumnAttribute.IgnoreMember(mi)) - .OrderBy(mi => XLColumnAttribute.GetOrder(mi)); - - memberCache.Add(itemType, _members); - accessorCache.Add(itemType, _accessor); - } - - members = memberCache[itemType]; - accessor = accessorCache[itemType]; if (transpose) rowNumber = Address.RowNumber; else columnNumber = Address.ColumnNumber; - if (itemType.IsPrimitive || itemType == typeof(String) || itemType == typeof(DateTime) || itemType.IsNumber()) { - SetValue(m, rowNumber, columnNumber); + _worksheet.SetValue(m, rowNumber, columnNumber); if (transpose) rowNumber++; @@ -779,7 +774,7 @@ { foreach (var item in (Array)m) { - SetValue(item, rowNumber, columnNumber); + _worksheet.SetValue(item, rowNumber, columnNumber); if (transpose) rowNumber++; @@ -794,7 +789,7 @@ foreach (var item in (m as DataRow).ItemArray) { - SetValue(item, rowNumber, columnNumber); + _worksheet.SetValue(item, rowNumber, columnNumber); if (transpose) rowNumber++; @@ -812,7 +807,7 @@ var fieldCount = record.FieldCount; for (var i = 0; i < fieldCount; i++) { - SetValue(record[i], rowNumber, columnNumber); + _worksheet.SetValue(record[i], rowNumber, columnNumber); if (transpose) rowNumber++; @@ -822,9 +817,30 @@ } else { + if (!memberCache.ContainsKey(itemType)) + { + var _accessor = TypeAccessor.Create(itemType); + + var _members = itemType.GetFields(bindingFlags).Cast() + .Concat(itemType.GetProperties(bindingFlags)) + .Where(mi => !XLColumnAttribute.IgnoreMember(mi)) + .OrderBy(mi => XLColumnAttribute.GetOrder(mi)); + + memberCache.Add(itemType, _members); + accessorCache.Add(itemType, _accessor); + } + + accessor = accessorCache[itemType]; + members = memberCache[itemType]; + foreach (var mi in members) { - SetValue(accessor[m, mi.Name], rowNumber, columnNumber); + if (mi.MemberType == MemberTypes.Property && (mi as PropertyInfo).GetGetMethod().IsStatic) + _worksheet.SetValue((mi as PropertyInfo).GetValue(null, null), rowNumber, columnNumber); + else if (mi.MemberType == MemberTypes.Field && (mi as FieldInfo).IsStatic) + _worksheet.SetValue((mi as FieldInfo).GetValue(null), rowNumber, columnNumber); + else + _worksheet.SetValue(accessor[m, mi.Name], rowNumber, columnNumber); if (transpose) rowNumber++; @@ -1180,10 +1196,15 @@ public Boolean IsEmpty(Boolean includeFormats) { + return IsEmpty(includeFormats, includeFormats); + } + + public Boolean IsEmpty(Boolean includeNormalFormats, Boolean includeConditionalFormats) + { if (InnerText.Length > 0) return false; - if (includeFormats) + if (includeNormalFormats) { if (!Style.Equals(Worksheet.Style) || IsMerged() || HasComment || HasDataValidation) return false; @@ -1198,10 +1219,12 @@ if (Worksheet.Internals.ColumnsCollection.TryGetValue(Address.ColumnNumber, out column) && !column.Style.Equals(Worksheet.Style)) return false; } - - if (Worksheet.ConditionalFormats.Any(cf => cf.Range.Contains(this))) - return false; } + + if (includeConditionalFormats + && Worksheet.ConditionalFormats.Any(cf => cf.Range.Contains(this))) + return false; + return true; } @@ -1499,7 +1522,7 @@ #endregion IXLStylized Members - private bool SetTableHeader(object value) + private Boolean SetTableHeaderValue(object value) { foreach (var table in Worksheet.Tables.Where(t => t.ShowHeaderRow)) { @@ -1516,6 +1539,26 @@ return false; } + private Boolean SetTableTotalsRowLabel(object value) + { + foreach (var table in Worksheet.Tables.Where(t => t.ShowTotalsRow)) + { + var cells = table.TotalsRow().Cells(c => c.Address.Equals(this.Address)); + if (cells.Any()) + { + var cell = cells.First(); + var field = table.Fields.First(f => f.Column.ColumnNumber() == cell.WorksheetColumn().ColumnNumber()); + field.TotalsRowFunction = XLTotalsRowFunction.None; + field.TotalsRowLabel = value.ToString(); + this._cellValue = value.ToString(); + this.DataType = XLCellValues.Text; + return true; + } + } + + return false; + } + private bool SetRangeColumns(object value) { var columns = value as XLRangeColumns; @@ -1581,14 +1624,57 @@ return _worksheet.Range(Address, Address); } + #region Styles + private IXLStyle GetStyle() { if (_style != null) return _style; - return _style = new XLStyle(this, Worksheet.Workbook.GetStyleById(_styleCacheId)); + return _style = new XLStyle(this, Worksheet.Workbook.GetStyleById(StyleCacheId())); } + private IXLStyle GetStyleForRead() + { + return Worksheet.Workbook.GetStyleById(GetStyleId()); + } + + public Int32 GetStyleId() + { + if (StyleChanged) + SetStyle(Style); + + return StyleCacheId(); + } + + private void SetStyle(IXLStyle styleToUse) + { + _styleCacheId = Worksheet.Workbook.GetStyleId(styleToUse); + _style = null; + StyleChanged = false; + } + + private void SetStyle(Int32 styleId) + { + _styleCacheId = styleId; + _style = null; + StyleChanged = false; + } + + public Int32 StyleCacheId() + { + if (!_styleCacheId.HasValue) + _styleCacheId = Worksheet.GetStyleId(); + return _styleCacheId.Value; + } + + public Boolean IsDefaultWorksheetStyle() + { + return !_styleCacheId.HasValue && !StyleChanged || GetStyleId() == Worksheet.GetStyleId(); + } + + #endregion Styles + public void DeleteComment() { _comment = null; @@ -1708,16 +1794,6 @@ mergeToDelete.ForEach(m => Worksheet.Internals.MergedRanges.Remove(m)); } - private void SetValue(T value, int ro, int co) where T : class - { - if (value == null) - _worksheet.Cell(ro, co).SetValue(String.Empty); - else if (value is IConvertible) - _worksheet.Cell(ro, co).SetValue((T)Convert.ChangeType(value, typeof(T))); - else - _worksheet.Cell(ro, co).SetValue(value); - } - private void SetValue(object value) { FormulaA1 = String.Empty; @@ -1795,6 +1871,10 @@ } } if (val.Length > 32767) throw new ArgumentException("Cells can only hold 32,767 characters."); + + if (SetTableHeaderValue(val)) return; + if (SetTableTotalsRowLabel(val)) return; + _cellValue = val; } @@ -2070,7 +2150,8 @@ CopyValuesFrom(source); - SetStyle(source._style ?? source.Worksheet.Workbook.GetStyleById(source._styleCacheId)); + if (source._styleCacheId.HasValue) + SetStyle(source._style ?? source.Worksheet.Workbook.GetStyleById(source._styleCacheId.Value)); var conditionalFormats = source.Worksheet.ConditionalFormats.Where(c => c.Range.Contains(source)).ToList(); foreach (var cf in conditionalFormats) diff --git a/ClosedXML/Excel/Cells/XLCells.cs b/ClosedXML/Excel/Cells/XLCells.cs index 5f49b98..c8fef10 100644 --- a/ClosedXML/Excel/Cells/XLCells.cs +++ b/ClosedXML/Excel/Cells/XLCells.cs @@ -52,16 +52,18 @@ { if (oneRange) { - var cellRange = range.Worksheet.Internals.CellsCollection - .GetCells( - range.FirstAddress.RowNumber, - range.FirstAddress.ColumnNumber, - range.LastAddress.RowNumber, - range.LastAddress.ColumnNumber) - .Where(c => - !c.IsEmpty(_includeFormats) - && (_predicate == null || _predicate(c)) - ); + var cellRange = range + .Worksheet + .Internals + .CellsCollection + .GetCells( + range.FirstAddress.RowNumber, + range.FirstAddress.ColumnNumber, + range.LastAddress.RowNumber, + range.LastAddress.ColumnNumber) + .Where(c => !c.IsEmpty(_includeFormats) + && (_predicate == null || _predicate(c)) + ); foreach(var cell in cellRange) { @@ -121,13 +123,16 @@ { if (_usedCellsOnly) { - var cellRange = cellsInRanges.SelectMany( - cir => - cir.Value.Select(a => cir.Key.Internals.CellsCollection.GetCell(a)).Where( - cell => cell != null && ( - !cell.IsEmpty(_includeFormats) - && (_predicate == null || _predicate(cell)) - ))); + var cellRange = cellsInRanges + .SelectMany( + cir => + cir.Value.Select(a => cir.Key.Internals.CellsCollection.GetCell(a)).Where( + cell => + cell != null + && !cell.IsEmpty(_includeFormats) + && (_predicate == null || _predicate(cell)) + ) + ); foreach (var cell in cellRange) { @@ -277,4 +282,4 @@ cell.Select(); } } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/Cells/XLCellsCollection.cs b/ClosedXML/Excel/Cells/XLCellsCollection.cs index 32a89aa..8f11a98 100644 --- a/ClosedXML/Excel/Cells/XLCellsCollection.cs +++ b/ClosedXML/Excel/Cells/XLCellsCollection.cs @@ -95,7 +95,7 @@ Count--; DecrementUsage(RowsUsed, row); DecrementUsage(ColumnsUsed, row); - + HashSet delHash; if (deleted.TryGetValue(row, out delHash)) { @@ -119,7 +119,7 @@ } } - + } internal IEnumerable GetCells(Int32 rowStart, Int32 columnStart, @@ -148,14 +148,22 @@ { HashSet ids = new HashSet(); ids.Add(initial); - foreach (var row in rowsCollection) + foreach (var row in rowsCollection.Values) { - foreach (var column in row.Value) + foreach (var cell in row.Values) { - var id = column.Value.GetStyleId(); - if (!ids.Contains(id)) + Int32? id = null; + + if (cell.StyleChanged) + id = cell.GetStyleId(); + else if (cell.StyleCacheId() != cell.Worksheet.GetStyleId()) { - ids.Add(id); + id = cell.GetStyleId(); + } + + if (id.HasValue && !ids.Contains(id.Value)) + { + ids.Add(id.Value); } } } @@ -494,4 +502,4 @@ return GetCells(row, 1, row, MaxColumnUsed); } } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/Coordinates/IXLAddress.cs b/ClosedXML/Excel/Coordinates/IXLAddress.cs index bc70a90..6158251 100644 --- a/ClosedXML/Excel/Coordinates/IXLAddress.cs +++ b/ClosedXML/Excel/Coordinates/IXLAddress.cs @@ -5,18 +5,27 @@ { public interface IXLAddress : IEqualityComparer, IEquatable { - IXLWorksheet Worksheet { get; } - Int32 RowNumber { get; } - Int32 ColumnNumber { get; } String ColumnLetter { get; } - Boolean FixedRow { get; } + Int32 ColumnNumber { get; } Boolean FixedColumn { get; } - String ToStringRelative(); - String ToStringRelative(Boolean includeSheet); - String ToStringFixed(); - String ToStringFixed(XLReferenceStyle referenceStyle); - String ToStringFixed(XLReferenceStyle referenceStyle, Boolean includeSheet); - String ToString(XLReferenceStyle referenceStyle); + Boolean FixedRow { get; } + Int32 RowNumber { get; } String UniqueId { get; } + IXLWorksheet Worksheet { get; } + + String ToString(XLReferenceStyle referenceStyle); + + String ToString(XLReferenceStyle referenceStyle, Boolean includeSheet); + + String ToStringFixed(); + + String ToStringFixed(XLReferenceStyle referenceStyle); + + String ToStringFixed(XLReferenceStyle referenceStyle, Boolean includeSheet); + + String ToStringRelative(); + + + String ToStringRelative(Boolean includeSheet); } } diff --git a/ClosedXML/Excel/Coordinates/XLAddress.cs b/ClosedXML/Excel/Coordinates/XLAddress.cs index b1d7ff8..81f4977 100644 --- a/ClosedXML/Excel/Coordinates/XLAddress.cs +++ b/ClosedXML/Excel/Coordinates/XLAddress.cs @@ -7,6 +7,7 @@ internal class XLAddress : IXLAddress { #region Static + /// /// Create address without worksheet. For calculation only! /// @@ -72,22 +73,32 @@ } return new XLAddress(worksheet, rowNumber, columnLetter, fixedRow, fixedColumn); } - #endregion + + #endregion Static + #region Private fields + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private bool _fixedRow; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private bool _fixedColumn; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string _columnLetter; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly int _rowNumber; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly int _columnNumber; + private string _trimmedAddress; - #endregion + + #endregion Private fields + #region Constructors + /// /// Initializes a new struct using a mixed notation. Attention: without worksheet for calculation only! /// @@ -99,6 +110,7 @@ : this(null, rowNumber, columnLetter, fixedRow, fixedColumn) { } + /// /// Initializes a new struct using a mixed notation. /// @@ -124,6 +136,7 @@ : this(null, rowNumber, columnNumber, fixedRow, fixedColumn) { } + /// /// Initializes a new struct using R1C1 notation. /// @@ -142,12 +155,14 @@ _columnLetter = null; _fixedColumn = fixedColumn; _fixedRow = fixedRow; - - } - #endregion + + #endregion Constructors + #region Properties + public XLWorksheet Worksheet { get; internal set; } + IXLWorksheet IXLAddress.Worksheet { [DebuggerStepThrough] @@ -195,8 +210,11 @@ { get { return _columnLetter ?? (_columnLetter = XLHelper.GetColumnLetterFromNumber(_columnNumber)); } } - #endregion + + #endregion Properties + #region Overrides + public override string ToString() { String retVal = ColumnLetter; @@ -214,31 +232,45 @@ public string ToString(XLReferenceStyle referenceStyle) { - if (referenceStyle == XLReferenceStyle.A1) - { - return ColumnLetter + _rowNumber.ToInvariantString(); - } - if (referenceStyle == XLReferenceStyle.R1C1) - { - return String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); - } - if (HasWorksheet && Worksheet.Workbook.ReferenceStyle == XLReferenceStyle.R1C1) - { - return String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); - } - return ColumnLetter + _rowNumber.ToInvariantString(); + return ToString(referenceStyle, false); } - #endregion + + public string ToString(XLReferenceStyle referenceStyle, bool includeSheet) + { + string address = string.Empty; + if (referenceStyle == XLReferenceStyle.A1) + + address = ColumnLetter + _rowNumber.ToInvariantString(); + else if (referenceStyle == XLReferenceStyle.R1C1) + + address = String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); + else if (HasWorksheet && Worksheet.Workbook.ReferenceStyle == XLReferenceStyle.R1C1) + + address = String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); + else + address = ColumnLetter + _rowNumber.ToInvariantString(); + + if (includeSheet) + return String.Format("{0}!{1}", + Worksheet.Name.WrapSheetNameInQuotesIfRequired(), + address); + + return address; + } + + #endregion Overrides + #region Methods + public string GetTrimmedAddress() { return _trimmedAddress ?? (_trimmedAddress = ColumnLetter + _rowNumber.ToInvariantString()); } + #endregion Methods - - #endregion #region Operator Overloads + public static XLAddress operator +(XLAddress left, XLAddress right) { return new XLAddress(left.Worksheet, @@ -288,9 +320,13 @@ { return !(left == right); } - #endregion + + #endregion Operator Overloads + #region Interface Requirements + #region IEqualityComparer Members + public Boolean Equals(IXLAddress x, IXLAddress y) { return x == y; @@ -315,8 +351,11 @@ { return _rowNumber ^ _columnNumber; } - #endregion + + #endregion IEqualityComparer Members + #region IEquatable Members + public bool Equals(IXLAddress other) { var right = other as XLAddress; @@ -329,10 +368,12 @@ public override Boolean Equals(Object other) { - return Equals((XLAddress) other); + return Equals((XLAddress)other); } - #endregion - #endregion + + #endregion IEquatable Members + + #endregion Interface Requirements public String ToStringRelative() { diff --git a/ClosedXML/Excel/Hyperlinks/XLHyperlink_Internal.cs b/ClosedXML/Excel/Hyperlinks/XLHyperlink_Internal.cs index 2e5bebe..caf81d2 100644 --- a/ClosedXML/Excel/Hyperlinks/XLHyperlink_Internal.cs +++ b/ClosedXML/Excel/Hyperlinks/XLHyperlink_Internal.cs @@ -5,7 +5,7 @@ public partial class XLHyperlink { internal XLHyperlink() - { + { } @@ -36,7 +36,7 @@ else { _internalAddress = address; - IsExternal = false; + IsExternal = false; } } } @@ -51,14 +51,14 @@ internal void SetValues(IXLCell cell, String tooltip) { Tooltip = tooltip; - _internalAddress = cell.Address.ToString(); + _internalAddress = cell.Address.ToString(XLReferenceStyle.A1, true); IsExternal = false; } internal void SetValues(IXLRangeBase range, String tooltip) { Tooltip = tooltip; - _internalAddress = range.RangeAddress.ToString(); + _internalAddress = range.RangeAddress.ToString(XLReferenceStyle.A1, true); IsExternal = false; } diff --git a/ClosedXML/Excel/PivotTables/IXLPivotFields.cs b/ClosedXML/Excel/PivotTables/IXLPivotFields.cs index 08c5312..df5a497 100644 --- a/ClosedXML/Excel/PivotTables/IXLPivotFields.cs +++ b/ClosedXML/Excel/PivotTables/IXLPivotFields.cs @@ -1,17 +1,22 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; namespace ClosedXML.Excel { - public interface IXLPivotFields: IEnumerable + public interface IXLPivotFields : IEnumerable { IXLPivotField Add(String sourceName); - IXLPivotField Add(String sourceName, String customName); - void Clear(); - void Remove(String sourceName); - int IndexOf(IXLPivotField pf); + IXLPivotField Add(String sourceName, String customName); + + void Clear(); + + Boolean Contains(String sourceName); + + IXLPivotField Get(String sourceName); + + Int32 IndexOf(IXLPivotField pf); + + void Remove(String sourceName); } } diff --git a/ClosedXML/Excel/PivotTables/XLPivotFields.cs b/ClosedXML/Excel/PivotTables/XLPivotFields.cs index c1a52f2..19accb7 100644 --- a/ClosedXML/Excel/PivotTables/XLPivotFields.cs +++ b/ClosedXML/Excel/PivotTables/XLPivotFields.cs @@ -1,13 +1,40 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace ClosedXML.Excel { - public class XLPivotFields: IXLPivotFields + public class XLPivotFields : IXLPivotFields { private readonly Dictionary _pivotFields = new Dictionary(); + + public IXLPivotField Add(String sourceName) + { + return Add(sourceName, sourceName); + } + + public IXLPivotField Add(String sourceName, String customName) + { + var pivotField = new XLPivotField(sourceName) { CustomName = customName }; + _pivotFields.Add(sourceName, pivotField); + return pivotField; + } + + public void Clear() + { + _pivotFields.Clear(); + } + + public Boolean Contains(String sourceName) + { + return _pivotFields.ContainsKey(sourceName); + } + + public IXLPivotField Get(string sourceName) + { + return _pivotFields[sourceName]; + } + public IEnumerator GetEnumerator() { return _pivotFields.Values.GetEnumerator(); @@ -18,32 +45,17 @@ return GetEnumerator(); } - public IXLPivotField Add(String sourceName) + public Int32 IndexOf(IXLPivotField pf) { - return Add(sourceName, sourceName); - } - public IXLPivotField Add(String sourceName, String customName) - { - var pivotField = new XLPivotField(sourceName) {CustomName = customName}; - _pivotFields.Add(sourceName, pivotField); - return pivotField; - } - - public void Clear() - { - _pivotFields.Clear(); - } - public void Remove(String sourceName) - { - _pivotFields.Remove(sourceName); - } - - public int IndexOf(IXLPivotField pf) - { - var selectedItem = _pivotFields.Select((item, index) => new {Item = item, Position = index}).FirstOrDefault(i => i.Item.Key == pf.SourceName); + var selectedItem = _pivotFields.Select((item, index) => new { Item = item, Position = index }).FirstOrDefault(i => i.Item.Key == pf.SourceName); if (selectedItem == null) throw new IndexOutOfRangeException("Invalid field name."); return selectedItem.Position; } + + public void Remove(String sourceName) + { + _pivotFields.Remove(sourceName); + } } } diff --git a/ClosedXML/Excel/Ranges/IXLRange.cs b/ClosedXML/Excel/Ranges/IXLRange.cs index 992ebf9..945c0c9 100644 --- a/ClosedXML/Excel/Ranges/IXLRange.cs +++ b/ClosedXML/Excel/Ranges/IXLRange.cs @@ -46,7 +46,7 @@ /// /// Gets the specified column of the range. /// - /// Column letter. + /// Column letter. IXLRangeColumn Column(string columnLetter); /// diff --git a/ClosedXML/Excel/Ranges/IXLRangeAddress.cs b/ClosedXML/Excel/Ranges/IXLRangeAddress.cs index 9da98e4..86ed2b1 100644 --- a/ClosedXML/Excel/Ranges/IXLRangeAddress.cs +++ b/ClosedXML/Excel/Ranges/IXLRangeAddress.cs @@ -4,8 +4,6 @@ { public interface IXLRangeAddress { - IXLWorksheet Worksheet { get; } - /// /// Gets or sets the first address in the range. /// @@ -13,13 +11,7 @@ /// The first address. /// IXLAddress FirstAddress { get; set; } - /// - /// Gets or sets the last address in the range. - /// - /// - /// The last address. - /// - IXLAddress LastAddress { get; set; } + /// /// Gets or sets a value indicating whether this range is invalid. /// @@ -28,10 +20,28 @@ /// Boolean IsInvalid { get; set; } - String ToStringRelative(); - String ToStringRelative(Boolean includeSheet); + /// + /// Gets or sets the last address in the range. + /// + /// + /// The last address. + /// + IXLAddress LastAddress { get; set; } + + IXLWorksheet Worksheet { get; } + + String ToString(XLReferenceStyle referenceStyle); + + String ToString(XLReferenceStyle referenceStyle, Boolean includeSheet); + String ToStringFixed(); + String ToStringFixed(XLReferenceStyle referenceStyle); + String ToStringFixed(XLReferenceStyle referenceStyle, Boolean includeSheet); + + String ToStringRelative(); + + String ToStringRelative(Boolean includeSheet); } } diff --git a/ClosedXML/Excel/Ranges/XLRangeAddress.cs b/ClosedXML/Excel/Ranges/XLRangeAddress.cs index fbe5135..13f986a 100644 --- a/ClosedXML/Excel/Ranges/XLRangeAddress.cs +++ b/ClosedXML/Excel/Ranges/XLRangeAddress.cs @@ -1,7 +1,6 @@ using ClosedXML.Extensions; using System; using System.Diagnostics; -using System.Globalization; using System.Linq; namespace ClosedXML.Excel @@ -10,16 +9,18 @@ { #region Private fields - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private XLAddress _firstAddress; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private XLAddress _lastAddress; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private XLAddress _firstAddress; - #endregion + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private XLAddress _lastAddress; + + #endregion Private fields #region Constructor - public XLRangeAddress(XLRangeAddress rangeAddress): this(rangeAddress.FirstAddress, rangeAddress.LastAddress) + public XLRangeAddress(XLRangeAddress rangeAddress) : this(rangeAddress.FirstAddress, rangeAddress.LastAddress) { - } public XLRangeAddress(XLAddress firstAddress, XLAddress lastAddress) @@ -73,7 +74,7 @@ Worksheet = worksheet; } - #endregion + #endregion Constructor #region Public properties @@ -122,10 +123,9 @@ set { LastAddress = value as XLAddress; } } - public bool IsInvalid { get; set; } - #endregion + #endregion Public properties #region Public methods @@ -168,7 +168,20 @@ public override string ToString() { - return _firstAddress + ":" + _lastAddress; + return String.Concat(_firstAddress, ':', _lastAddress); + } + + public string ToString(XLReferenceStyle referenceStyle) + { + return ToString(referenceStyle, false); + } + + public string ToString(XLReferenceStyle referenceStyle, bool includeSheet) + { + if (referenceStyle == XLReferenceStyle.R1C1) + return ToStringFixed(referenceStyle, true); + else + return ToStringRelative(includeSheet); } public override bool Equals(object obj) @@ -187,6 +200,6 @@ ^ LastAddress.GetHashCode(); } - #endregion + #endregion Public methods } } diff --git a/ClosedXML/Excel/Ranges/XLRangeBase.cs b/ClosedXML/Excel/Ranges/XLRangeBase.cs index 0534107..190d473 100644 --- a/ClosedXML/Excel/Ranges/XLRangeBase.cs +++ b/ClosedXML/Excel/Ranges/XLRangeBase.cs @@ -841,12 +841,14 @@ Int32 newCellStyleId = styleId; + XLCell newCell; // If the default style for this range base is empty, but the worksheet // has a default style, use the worksheet's default style - if (styleId == 0 && worksheetStyleId != 0) - newCellStyleId = worksheetStyleId; + if (styleId == 0 && worksheetStyleId != 0 || styleId == worksheetStyleId) + newCell = new XLCell(Worksheet, absoluteAddress); + else + newCell = new XLCell(Worksheet, absoluteAddress, newCellStyleId); - var newCell = new XLCell(Worksheet, absoluteAddress, newCellStyleId); Worksheet.Internals.CellsCollection.Add(absRow, absColumn, newCell); return newCell; } diff --git a/ClosedXML/Excel/Tables/IXLTable.cs b/ClosedXML/Excel/Tables/IXLTable.cs index 386c669..1030c02 100644 --- a/ClosedXML/Excel/Tables/IXLTable.cs +++ b/ClosedXML/Excel/Tables/IXLTable.cs @@ -1,31 +1,22 @@ using System; using System.Collections.Generic; + namespace ClosedXML.Excel { public interface IXLTable : IXLRange { - string Name { get; set; } + IXLBaseAutoFilter AutoFilter { get; } + IXLTableRange DataRange { get; } Boolean EmphasizeFirstColumn { get; set; } Boolean EmphasizeLastColumn { get; set; } - Boolean ShowRowStripes { get; set; } - Boolean ShowColumnStripes { get; set; } - Boolean ShowTotalsRow { get; set; } - Boolean ShowAutoFilter { get; set; } - XLTableTheme Theme { get; set; } - IXLRangeRow HeadersRow(); - IXLRangeRow TotalsRow(); - IXLTableField Field(string fieldName); - IXLTableField Field(int fieldIndex); IEnumerable Fields { get; } - - - - IXLTable SetEmphasizeFirstColumn(); IXLTable SetEmphasizeFirstColumn(Boolean value); - IXLTable SetEmphasizeLastColumn(); IXLTable SetEmphasizeLastColumn(Boolean value); - IXLTable SetShowRowStripes(); IXLTable SetShowRowStripes(Boolean value); - IXLTable SetShowColumnStripes(); IXLTable SetShowColumnStripes(Boolean value); - IXLTable SetShowTotalsRow(); IXLTable SetShowTotalsRow(Boolean value); - IXLTable SetShowAutoFilter(); IXLTable SetShowAutoFilter(Boolean value); + string Name { get; set; } + Boolean ShowAutoFilter { get; set; } + Boolean ShowColumnStripes { get; set; } + Boolean ShowHeaderRow { get; set; } + Boolean ShowRowStripes { get; set; } + Boolean ShowTotalsRow { get; set; } + XLTableTheme Theme { get; set; } /// /// Clears the contents of this table. @@ -33,13 +24,97 @@ /// Specify what you want to clear. new IXLTable Clear(XLClearOptions clearOptions = XLClearOptions.ContentsAndFormats); - IXLBaseAutoFilter AutoFilter { get; } + IXLTableField Field(string fieldName); + + IXLTableField Field(int fieldIndex); + + IXLRangeRow HeadersRow(); + + /// + /// Resizes the table to the specified range. + /// + /// The new table range. + /// + IXLTable Resize(IXLRange range); + + /// + /// Resizes the table to the specified range address. + /// + /// The range boundaries. + /// + IXLTable Resize(IXLRangeAddress rangeAddress); + + /// + /// Resizes the table to the specified range address. + /// + /// The range boundaries. + /// + IXLTable Resize(string rangeAddress); + + /// + /// Resizes the table to the specified range. + /// + /// The first cell in the range. + /// The last cell in the range. + /// + IXLTable Resize(IXLCell firstCell, IXLCell lastCell); + + /// + /// Resizes the table to the specified range. + /// + /// The first cell address in the worksheet. + /// The last cell address in the worksheet. + /// + IXLTable Resize(string firstCellAddress, string lastCellAddress); + + /// + /// Resizes the table to the specified range. + /// + /// The first cell address in the worksheet. + /// The last cell address in the worksheet. + /// + IXLTable Resize(IXLAddress firstCellAddress, IXLAddress lastCellAddress); + + /// + /// Resizes the table to the specified range. + /// + /// The first cell's row of the range to return. + /// The first cell's column of the range to return. + /// The last cell's row of the range to return. + /// The last cell's column of the range to return. + /// + IXLTable Resize(int firstCellRow, int firstCellColumn, int lastCellRow, int lastCellColumn); new IXLBaseAutoFilter SetAutoFilter(); - Boolean ShowHeaderRow { get; set; } - IXLTable SetShowHeaderRow(); IXLTable SetShowHeaderRow(Boolean value); + IXLTable SetEmphasizeFirstColumn(); - IXLTableRange DataRange { get; } + IXLTable SetEmphasizeFirstColumn(Boolean value); + + IXLTable SetEmphasizeLastColumn(); + + IXLTable SetEmphasizeLastColumn(Boolean value); + + IXLTable SetShowAutoFilter(); + + IXLTable SetShowAutoFilter(Boolean value); + + IXLTable SetShowColumnStripes(); + + IXLTable SetShowColumnStripes(Boolean value); + + IXLTable SetShowHeaderRow(); + + IXLTable SetShowHeaderRow(Boolean value); + + IXLTable SetShowRowStripes(); + + IXLTable SetShowRowStripes(Boolean value); + + IXLTable SetShowTotalsRow(); + + IXLTable SetShowTotalsRow(Boolean value); + + IXLRangeRow TotalsRow(); } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/Tables/XLTable.cs b/ClosedXML/Excel/Tables/XLTable.cs index 6048e33..c468d76 100644 --- a/ClosedXML/Excel/Tables/XLTable.cs +++ b/ClosedXML/Excel/Tables/XLTable.cs @@ -54,43 +54,76 @@ { get { - if (_fieldNames != null && _lastRangeAddress != null && _lastRangeAddress.Equals(RangeAddress)) return _fieldNames; + if (_fieldNames != null && _lastRangeAddress != null && _lastRangeAddress.Equals(RangeAddress)) + return _fieldNames; - _fieldNames = new Dictionary(); - _lastRangeAddress = RangeAddress; - - if (ShowHeaderRow) + if (_fieldNames == null) { - var headersRow = HeadersRow(); - Int32 cellPos = 0; - foreach (var cell in headersRow.Cells()) - { - var name = cell.GetString(); - if (String.IsNullOrWhiteSpace(name)) - { - name = "Column" + (cellPos + 1); - cell.SetValue(name); - } - if (_fieldNames.ContainsKey(name)) - throw new ArgumentException("The header row contains more than one field name '" + name + "'."); - - _fieldNames.Add(name, new XLTableField(this, name) { Index = cellPos++ }); - } + _fieldNames = new Dictionary(); + _lastRangeAddress = RangeAddress; + HeadersRow(); } else { - Int32 colCount = ColumnCount(); - for (Int32 i = 1; i <= colCount; i++) - { - if (!_fieldNames.Values.Any(f => f.Index == i - 1)) - { - var name = "Column" + i; + HeadersRow(false); + } - _fieldNames.Add(name, new XLTableField(this, name) { Index = i - 1 }); - } + RescanFieldNames(); + + _lastRangeAddress = RangeAddress; + + return _fieldNames; + } + } + + private void RescanFieldNames() + { + if (ShowHeaderRow) + { + var detectedFieldNames = new Dictionary(); + var headersRow = HeadersRow(false); + Int32 cellPos = 0; + foreach (var cell in headersRow.Cells()) + { + var name = cell.GetString(); + if (_fieldNames.ContainsKey(name) && _fieldNames[name].Column.ColumnNumber() == cell.Address.ColumnNumber) + { + (_fieldNames[name] as XLTableField).Index = cellPos; + detectedFieldNames.Add(name, _fieldNames[name]); + cellPos++; + continue; + } + + if (String.IsNullOrWhiteSpace(name)) + { + name = GetUniqueName("Column", cellPos + 1, true); + cell.SetValue(name); + cell.DataType = XLCellValues.Text; + } + if (_fieldNames.ContainsKey(name)) + throw new ArgumentException("The header row contains more than one field name '" + name + "'."); + + _fieldNames.Add(name, new XLTableField(this, name) { Index = cellPos++ }); + detectedFieldNames.Add(name, _fieldNames[name]); + } + + _fieldNames.Keys + .Where(key => !detectedFieldNames.ContainsKey(key)) + .ToArray() + .ForEach(key => _fieldNames.Remove(key)); + } + else + { + Int32 colCount = ColumnCount(); + for (Int32 i = 1; i <= colCount; i++) + { + if (!_fieldNames.Values.Any(f => f.Index == i - 1)) + { + var name = "Column" + i; + + _fieldNames.Add(name, new XLTableField(this, name) { Index = i - 1 }); } } - return _fieldNames; } } @@ -122,9 +155,6 @@ get { XLRange range; - //var ws = Worksheet; - //var tracking = ws.EventTrackingEnabled; - //ws.EventTrackingEnabled = false; if (_showHeaderRow) { @@ -138,7 +168,7 @@ ? Range(1, 1, RowCount() - 1, ColumnCount()) : Range(1, 1, RowCount(), ColumnCount()); } - //ws.EventTrackingEnabled = tracking; + return new XLTableRange(range, this); } } @@ -222,9 +252,18 @@ public IXLRangeRow HeadersRow() { + return HeadersRow(true); + } + + internal IXLRangeRow HeadersRow(Boolean scanForNewFieldsNames) + { if (!ShowHeaderRow) return null; - var m = FieldNames; + if (scanForNewFieldsNames) + { + var tempResult = FieldNames; + } + return FirstRow(); } @@ -253,6 +292,124 @@ } } + public IXLTable Resize(IXLRangeAddress rangeAddress) + { + return Resize(Worksheet.Range(RangeAddress)); + } + + public IXLTable Resize(string rangeAddress) + { + return Resize(Worksheet.Range(RangeAddress)); + } + + public IXLTable Resize(IXLCell firstCell, IXLCell lastCell) + { + return Resize(Worksheet.Range(firstCell, lastCell)); + } + + public IXLTable Resize(string firstCellAddress, string lastCellAddress) + { + return Resize(Worksheet.Range(firstCellAddress, lastCellAddress)); + } + + public IXLTable Resize(IXLAddress firstCellAddress, IXLAddress lastCellAddress) + { + return Resize(Worksheet.Range(firstCellAddress, lastCellAddress)); + } + + public IXLTable Resize(int firstCellRow, int firstCellColumn, int lastCellRow, int lastCellColumn) + { + return Resize(Worksheet.Range(firstCellRow, firstCellColumn, lastCellRow, lastCellColumn)); + } + + public IXLTable Resize(IXLRange range) + { + if (!this.ShowHeaderRow) + throw new NotImplementedException("Resizing of tables with no headers not supported yet."); + + if (this.Worksheet != range.Worksheet) + throw new InvalidOperationException("You cannot resize a table to a range on a different sheet."); + + var totalsRowChanged = this.ShowTotalsRow ? range.LastRow().RowNumber() - this.TotalsRow().RowNumber() : 0; + var oldTotalsRowNumber = this.ShowTotalsRow ? this.TotalsRow().RowNumber() : -1; + + var existingHeaders = this.FieldNames.Keys; + var newHeaders = new HashSet(); + var tempArray = this.Fields.Select(f => f.Column).ToArray(); + + var firstRow = range.Row(1); + if (!firstRow.FirstCell().Address.Equals(this.HeadersRow().FirstCell().Address) + || !firstRow.LastCell().Address.Equals(this.HeadersRow().LastCell().Address)) + { + _uniqueNames.Clear(); + var co = 1; + foreach (var c in firstRow.Cells()) + { + if (String.IsNullOrWhiteSpace(((XLCell)c).InnerText)) + c.Value = GetUniqueName("Column", co, true); + + var header = c.GetString(); + _uniqueNames.Add(header); + + if (!existingHeaders.Contains(header)) + newHeaders.Add(header); + + co++; + } + } + + if (totalsRowChanged < 0) + { + range.Rows(r => r.RowNumber().Equals(this.TotalsRow().RowNumber() + totalsRowChanged)).Single().InsertRowsAbove(1); + range = Worksheet.Range(range.FirstCell(), range.LastCell().CellAbove()); + oldTotalsRowNumber++; + } + else if (totalsRowChanged > 0) + { + this.TotalsRow().RowBelow(totalsRowChanged + 1).InsertRowsAbove(1); + this.TotalsRow().AsRange().Delete(XLShiftDeletedCells.ShiftCellsUp); + } + + this.RangeAddress = range.RangeAddress as XLRangeAddress; + RescanFieldNames(); + + if (this.ShowTotalsRow) + { + foreach (var f in this._fieldNames.Values) + { + var c = this.TotalsRow().Cell(f.Index + 1); + if (!c.IsEmpty() && newHeaders.Contains(f.Name)) + { + f.TotalsRowLabel = c.GetFormattedString(); + c.DataType = XLCellValues.Text; + } + } + + if (totalsRowChanged != 0) + { + foreach (var f in this._fieldNames.Values.Cast()) + { + f.UpdateUnderlyingCellFormula(); + var c = this.TotalsRow().Cell(f.Index + 1); + if (!String.IsNullOrWhiteSpace(f.TotalsRowLabel)) + { + c.DataType = XLCellValues.Text; + + //Remove previous row's label + var oldTotalsCell = this.Worksheet.Cell(oldTotalsRowNumber, f.Column.ColumnNumber()); + if (oldTotalsCell.Value.ToString() == f.TotalsRowLabel) + oldTotalsCell.Value = null; + } + + if (f.TotalsRowFunction != XLTotalsRowFunction.None) + c.DataType = XLCellValues.Number; + } + } + } + + return this; + } + public IXLTable SetEmphasizeFirstColumn() { EmphasizeFirstColumn = true; @@ -386,7 +543,7 @@ if (setAutofilter) InitializeAutoFilter(); - HeadersRow().DataType = XLCellValues.Text; + AsRange().Row(1).DataType = XLCellValues.Text; if (RowCount() == 1) InsertRowsBelow(1); @@ -406,19 +563,19 @@ foreach (IXLCell c in range.Row(1).Cells()) { if (String.IsNullOrWhiteSpace(((XLCell)c).InnerText)) - c.Value = GetUniqueName("Column" + co.ToInvariantString()); + c.Value = GetUniqueName("Column", co, true); _uniqueNames.Add(c.GetString()); co++; } Worksheet.Tables.Add(this); } - private String GetUniqueName(String originalName) + private String GetUniqueName(String originalName, Int32 initialOffset, Boolean enforceOffset) { - String name = originalName; - if (_uniqueNames.Contains(name)) + String name = String.Concat(originalName, enforceOffset ? initialOffset.ToInvariantString() : string.Empty); + if (_uniqueNames?.Contains(name) ?? false) { - Int32 i = 1; + Int32 i = initialOffset; name = originalName + i.ToInvariantString(); while (_uniqueNames.Contains(name)) { @@ -427,7 +584,6 @@ } } - _uniqueNames.Add(name); return name; } @@ -460,7 +616,7 @@ foreach (IXLCell c in headersRow.Cells()) { if (String.IsNullOrWhiteSpace(((XLCell)c).InnerText)) - c.Value = GetUniqueName("Column" + co.ToInvariantString()); + c.Value = GetUniqueName("Column", co, true); _uniqueNames.Add(c.GetString()); co++; } diff --git a/ClosedXML/Excel/Tables/XLTableField.cs b/ClosedXML/Excel/Tables/XLTableField.cs index 528d7d2..c737adc 100644 --- a/ClosedXML/Excel/Tables/XLTableField.cs +++ b/ClosedXML/Excel/Tables/XLTableField.cs @@ -19,12 +19,25 @@ this.name = name; } + private IXLRangeColumn _column; + public IXLRangeColumn Column { - get { return table.Column(this.Index); } + get { return _column ?? (_column = table.HeadersRow(false).Cell(this.Index + 1).AsRange().Columns().Single()); } } - public Int32 Index { get; internal set; } + private Int32 index; + + public Int32 Index + { + get { return index; } + internal set + { + if (index == value) return; + index = value; + _column = null; + } + } public String Name { @@ -35,7 +48,7 @@ set { if (table.ShowHeaderRow) - table.HeadersRow().Cell(Index + 1).SetValue(value); + (table.HeadersRow(false).Cell(Index + 1) as XLCell).SetValue(value, false); table.RenameField(name, value); name = value; @@ -67,31 +80,36 @@ get { return totalsRowFunction; } set { - if (value != XLTotalsRowFunction.None && value != XLTotalsRowFunction.Custom) - { - var cell = table.TotalsRow().Cell(Index + 1); - String formula = String.Empty; - switch (value) - { - case XLTotalsRowFunction.Sum: formula = "109"; break; - case XLTotalsRowFunction.Minimum: formula = "105"; break; - case XLTotalsRowFunction.Maximum: formula = "104"; break; - case XLTotalsRowFunction.Average: formula = "101"; break; - case XLTotalsRowFunction.Count: formula = "103"; break; - case XLTotalsRowFunction.CountNumbers: formula = "102"; break; - case XLTotalsRowFunction.StandardDeviation: formula = "107"; break; - case XLTotalsRowFunction.Variance: formula = "110"; break; - } - - cell.FormulaA1 = "SUBTOTAL(" + formula + ",[" + Name + "])"; - var lastCell = table.LastRow().Cell(Index + 1); - if (lastCell.DataType != XLCellValues.Text) - { - cell.DataType = lastCell.DataType; - cell.Style.NumberFormat = lastCell.Style.NumberFormat; - } - } totalsRowFunction = value; + UpdateUnderlyingCellFormula(); + } + } + + internal void UpdateUnderlyingCellFormula() + { + if (TotalsRowFunction != XLTotalsRowFunction.None && TotalsRowFunction != XLTotalsRowFunction.Custom) + { + var cell = table.TotalsRow().Cell(Index + 1); + String formula = String.Empty; + switch (TotalsRowFunction) + { + case XLTotalsRowFunction.Sum: formula = "109"; break; + case XLTotalsRowFunction.Minimum: formula = "105"; break; + case XLTotalsRowFunction.Maximum: formula = "104"; break; + case XLTotalsRowFunction.Average: formula = "101"; break; + case XLTotalsRowFunction.Count: formula = "103"; break; + case XLTotalsRowFunction.CountNumbers: formula = "102"; break; + case XLTotalsRowFunction.StandardDeviation: formula = "107"; break; + case XLTotalsRowFunction.Variance: formula = "110"; break; + } + + cell.FormulaA1 = "SUBTOTAL(" + formula + ",[" + Name + "])"; + var lastCell = table.LastRow().Cell(Index + 1); + if (lastCell.DataType != XLCellValues.Text) + { + cell.DataType = lastCell.DataType; + cell.Style.NumberFormat = lastCell.Style.NumberFormat; + } } } @@ -101,7 +119,7 @@ set { totalsRowFunction = XLTotalsRowFunction.None; - table.TotalsRow().Cell(Index + 1).SetValue(value); + (table.TotalsRow().Cell(Index + 1) as XLCell).SetValue(value, false); totalsRowLabel = value; } } @@ -113,12 +131,16 @@ internal void Delete(Boolean deleteUnderlyingRangeColumn) { - var fields = table.Fields.Cast(); - fields.Where(f => f.Index > this.Index).ForEach(f => f.Index--); - table.FieldNames.Remove(this.Name); + var fields = table.Fields.Cast().ToArray(); if (deleteUnderlyingRangeColumn) - (this.Column as XLRangeColumn).Delete(false); + { + table.AsRange().ColumnQuick(this.Index + 1).Delete(); + // (this.Column as XLRangeColumn).Delete(false); + } + + fields.Where(f => f.Index > this.Index).ForEach(f => f.Index--); + table.FieldNames.Remove(this.Name); } } -} +} \ No newline at end of file diff --git a/ClosedXML/Excel/XLWorkbook_Save.cs b/ClosedXML/Excel/XLWorkbook_Save.cs index 8becefc..fab3b90 100644 --- a/ClosedXML/Excel/XLWorkbook_Save.cs +++ b/ClosedXML/Excel/XLWorkbook_Save.cs @@ -150,7 +150,6 @@ if (worksheet == null) return; - string sheetName = worksheet.Name; // Get the pivot Table Parts IEnumerable pvtTableCacheParts = wbPart.PivotTableCacheDefinitionParts; @@ -208,11 +207,9 @@ foreach (CalculationCell Item in calChainEntries) calcsToDelete.Add(Item); - foreach (CalculationCell Item in calcsToDelete) Item.Remove(); - if (!calChainPart.CalculationChain.Any()) wbPart.DeletePart(calChainPart); } @@ -681,6 +678,8 @@ var xlSheet = Worksheet(sheet.Name); if (xlSheet.Visibility != XLWorksheetVisibility.Visible) sheet.State = xlSheet.Visibility.ToOpenXml(); + else + sheet.State = null; if (foundVisible) continue; @@ -1981,7 +1980,11 @@ { var columnNumber = c.ColumnNumber(); var columnName = c.FirstCell().Value.ToString(); - var xlpf = pt.Fields.Add(columnName); + IXLPivotField xlpf; + if (pt.Fields.Contains(columnName)) + xlpf = pt.Fields.Get(columnName); + else + xlpf = pt.Fields.Add(columnName); var field = pt.RowLabels.Union(pt.ColumnLabels).Union(pt.ReportFilters).FirstOrDefault(f => f.SourceName == columnName); @@ -2167,10 +2170,12 @@ case XLPivotSubtotals.DoNotShow: pf.DefaultSubtotal = false; break; + case XLPivotSubtotals.AtBottom: pf.DefaultSubtotal = true; pf.SubtotalTop = false; break; + case XLPivotSubtotals.AtTop: pf.DefaultSubtotal = true; pf.SubtotalTop = true; @@ -4165,15 +4170,15 @@ cm.SetElement(XLWSContentManager.XLWSContents.SheetData, sheetData); var lastRow = 0; - var sheetDataRows = + var existingSheetDataRows = sheetData.Elements().ToDictionary(r => r.RowIndex == null ? ++lastRow : (Int32)r.RowIndex.Value, r => r); foreach ( var r in - xlWorksheet.Internals.RowsCollection.Deleted.Where(r => sheetDataRows.ContainsKey(r.Key))) + xlWorksheet.Internals.RowsCollection.Deleted.Where(r => existingSheetDataRows.ContainsKey(r.Key))) { - sheetData.RemoveChild(sheetDataRows[r.Key]); - sheetDataRows.Remove(r.Key); + sheetData.RemoveChild(existingSheetDataRows[r.Key]); + existingSheetDataRows.Remove(r.Key); xlWorksheet.Internals.CellsCollection.deleted.Remove(r.Key); } @@ -4182,28 +4187,10 @@ foreach (var distinctRow in distinctRows.OrderBy(r => r)) { Row row; - if (sheetDataRows.ContainsKey(distinctRow)) - row = sheetDataRows[distinctRow]; + if (existingSheetDataRows.ContainsKey(distinctRow)) + row = existingSheetDataRows[distinctRow]; else - { row = new Row { RowIndex = (UInt32)distinctRow }; - if (noRows) - { - sheetData.AppendChild(row); - noRows = false; - } - else - { - if (sheetDataRows.Any(r => r.Key > row.RowIndex.Value)) - { - var minRow = sheetDataRows.Where(r => r.Key > (Int32)row.RowIndex.Value).Min(r => r.Key); - var rowBeforeInsert = sheetDataRows[minRow]; - sheetData.InsertBefore(row, rowBeforeInsert); - } - else - sheetData.AppendChild(row); - } - } if (maxColumn > 0) row.Spans = new ListValue { InnerText = "1:" + maxColumn.ToInvariantString() }; @@ -4257,8 +4244,8 @@ xlWorksheet.Internals.CellsCollection.deleted.Remove(kpDel.Key); } - if (!xlWorksheet.Internals.CellsCollection.RowsCollection.ContainsKey(distinctRow)) continue; - + if (xlWorksheet.Internals.CellsCollection.RowsCollection.ContainsKey(distinctRow)) + { var isNewRow = !row.Elements().Any(); lastCell = 0; var mRows = row.Elements().ToDictionary(c => XLHelper.GetColumnNumberFromAddress(c.CellReference == null @@ -4267,9 +4254,13 @@ .OrderBy(c => c.Address.ColumnNumber) .Select(c => c)) { + XLTableField field = null; + var styleId = context.SharedStyles[xlCell.GetStyleId()].StyleId; var cellReference = (xlCell.Address).GetTrimmedAddress(); - var isEmpty = xlCell.IsEmpty(true); + + // For saving cells to file, ignore conditional formatting. They just bloat the file + var isEmpty = xlCell.IsEmpty(true, false); Cell cell = null; if (cellsByReference.ContainsKey(cellReference)) @@ -4336,25 +4327,75 @@ cell.CellValue = null; } + else if (xlCell.TableCellType() == XLTableCellType.Total) + { + var table = xlWorksheet.Tables.First(t => t.AsRange().Contains(xlCell)); + field = table.Fields.First(f => f.Column.ColumnNumber() == xlCell.Address.ColumnNumber) as XLTableField; + + if (!String.IsNullOrWhiteSpace(field.TotalsRowLabel)) + { + cell.DataType = XLWorkbook.CvSharedString; + } else { + cell.DataType = null; + } cell.CellFormula = null; + } + else + { + cell.CellFormula = null; cell.DataType = xlCell.DataType == XLCellValues.DateTime ? null : GetCellValueType(xlCell); } - if (!xlCell.HasFormula || evaluateFormulae) - SetCellValue(xlCell, cell); - } - } - xlWorksheet.Internals.CellsCollection.deleted.Remove(distinctRow); + if (evaluateFormulae || field != null || !xlCell.HasFormula) + SetCellValue(xlCell, field, cell); + } + } + xlWorksheet.Internals.CellsCollection.deleted.Remove(distinctRow); + } + + // If we're adding a new row (not in sheet already and it's not "empty" + if (!existingSheetDataRows.ContainsKey(distinctRow)) + { + var invalidRow = row.Height == null + && row.CustomHeight == null + && row.Hidden == null + && row.StyleIndex == null + && row.CustomFormat == null + && row.Collapsed == null + && row.OutlineLevel == null + && !row.Elements().Any(); + + if (!invalidRow) + { + if (noRows) + { + sheetData.AppendChild(row); + noRows = false; + } + else + { + if (existingSheetDataRows.Any(r => r.Key > row.RowIndex.Value)) + { + var minRow = existingSheetDataRows.Where(r => r.Key > (Int32)row.RowIndex.Value).Min(r => r.Key); + var rowBeforeInsert = existingSheetDataRows[minRow]; + sheetData.InsertBefore(row, rowBeforeInsert); + } + else + sheetData.AppendChild(row); + } + } + } } + foreach ( var r in xlWorksheet.Internals.CellsCollection.deleted.Keys.Where( - sheetDataRows.ContainsKey)) + existingSheetDataRows.ContainsKey)) { - sheetData.RemoveChild(sheetDataRows[r]); - sheetDataRows.Remove(r); + sheetData.RemoveChild(existingSheetDataRows[r]); + existingSheetDataRows.Remove(r); } #endregion SheetData @@ -4859,8 +4900,25 @@ #endregion LegacyDrawingHeaderFooter } - private static void SetCellValue(XLCell xlCell, Cell openXmlCell) + private static void SetCellValue(XLCell xlCell, XLTableField field, Cell openXmlCell) { + if (field != null) + { + if (!String.IsNullOrWhiteSpace(field.TotalsRowLabel)) + { + var cellValue = new CellValue(); + cellValue.Text = xlCell.SharedStringId.ToString(); + openXmlCell.DataType = CvSharedString; + openXmlCell.CellValue = cellValue; + } + else if (field.TotalsRowFunction == XLTotalsRowFunction.None) + { + openXmlCell.DataType = CvSharedString; + openXmlCell.CellValue = null; + } + return; + } + if (xlCell.HasFormula) { var cellValue = new CellValue(); diff --git a/ClosedXML/Excel/XLWorksheet.cs b/ClosedXML/Excel/XLWorksheet.cs index 0296632..6945ddc 100644 --- a/ClosedXML/Excel/XLWorksheet.cs +++ b/ClosedXML/Excel/XLWorksheet.cs @@ -1597,5 +1597,16 @@ { return true; } + + internal void SetValue(T value, int ro, int co) where T : class + { + if (value == null) + this.Cell(ro, co).SetValue(String.Empty); + else if (value is IConvertible) + this.Cell(ro, co).SetValue((T)Convert.ChangeType(value, typeof(T))); + else + this.Cell(ro, co).SetValue(value); + } + } } diff --git a/ClosedXML_Examples/Creating/CreateFiles.cs b/ClosedXML_Examples/Creating/CreateFiles.cs index 1e639dd..8f1978f 100644 --- a/ClosedXML_Examples/Creating/CreateFiles.cs +++ b/ClosedXML_Examples/Creating/CreateFiles.cs @@ -4,6 +4,7 @@ using ClosedXML_Examples.Ranges; using ClosedXML_Examples.Rows; using ClosedXML_Examples.Styles; +using ClosedXML_Examples.Tables; using System.IO; namespace ClosedXML_Examples @@ -67,6 +68,7 @@ new RowCells().Create(Path.Combine(path, "RowCells.xlsx")); new FreezePanes().Create(Path.Combine(path, "FreezePanes.xlsx")); new UsingTables().Create(Path.Combine(path, "UsingTables.xlsx")); + new ResizingTables().Create(Path.Combine(path, "ResizingTables.xlsx")); new AddingRowToTables().Create(Path.Combine(path, "AddingRowToTables.xlsx")); new RightToLeft().Create(Path.Combine(path, "RightToLeft.xlsx")); new ShowCase().Create(Path.Combine(path, "ShowCase.xlsx")); diff --git a/ClosedXML_Examples/Misc/CopyingWorksheets.cs b/ClosedXML_Examples/Misc/CopyingWorksheets.cs index b0843eb..e06cd66 100644 --- a/ClosedXML_Examples/Misc/CopyingWorksheets.cs +++ b/ClosedXML_Examples/Misc/CopyingWorksheets.cs @@ -1,6 +1,6 @@ -using System.IO; using ClosedXML.Excel; -using ClosedXML_Examples.Ranges; +using ClosedXML_Examples.Tables; +using System.IO; namespace ClosedXML_Examples.Misc { @@ -18,7 +18,7 @@ var wsSource = wb.Worksheet(1); // Copy the worksheet to a new sheet in this workbook wsSource.CopyTo("Copy"); - + // We're going to open another workbook to show that you can // copy a sheet from one workbook to another: new BasicTable().Create(tempFile2); @@ -40,6 +40,5 @@ } } } - } } diff --git a/ClosedXML_Examples/Misc/InsertingData.cs b/ClosedXML_Examples/Misc/InsertingData.cs index 8ce9946..626a9ec 100644 --- a/ClosedXML_Examples/Misc/InsertingData.cs +++ b/ClosedXML_Examples/Misc/InsertingData.cs @@ -1,8 +1,8 @@ +using ClosedXML.Excel; using System; using System.Collections.Generic; using System.Data; using System.Linq; -using ClosedXML.Excel; namespace ClosedXML_Examples.Misc { @@ -15,76 +15,80 @@ { using (var wb = new XLWorkbook()) { - var ws = wb.Worksheets.Add("Inserting Data"); + var ws = wb.Worksheets.Add("Inserting Data"); - // From a list of strings - var listOfStrings = new List(); - listOfStrings.Add("House"); - listOfStrings.Add("001"); - ws.Cell(1, 1).Value = "From Strings"; - ws.Cell(1, 1).AsRange().AddToNamed("Titles"); - ws.Cell(2, 1).InsertData(listOfStrings); + // From a list of strings + var listOfStrings = new List(); + listOfStrings.Add("House"); + listOfStrings.Add("001"); + ws.Cell(1, 1).Value = "From Strings"; + ws.Cell(1, 1).AsRange().AddToNamed("Titles"); + ws.Cell(2, 1).InsertData(listOfStrings); - // From a list of arrays - var listOfArr = new List(); - listOfArr.Add(new Int32[] { 1, 2, 3 }); - listOfArr.Add(new Int32[] { 1 }); - listOfArr.Add(new Int32[] { 1, 2, 3, 4, 5, 6 }); - ws.Cell(1, 3).Value = "From Arrays"; - ws.Range(1, 3, 1, 8).Merge().AddToNamed("Titles"); - ws.Cell(2, 3).InsertData(listOfArr); + // From a list of arrays + var listOfArr = new List(); + listOfArr.Add(new Int32[] { 1, 2, 3 }); + listOfArr.Add(new Int32[] { 1 }); + listOfArr.Add(new Int32[] { 1, 2, 3, 4, 5, 6 }); + ws.Cell(1, 3).Value = "From Arrays"; + ws.Range(1, 3, 1, 8).Merge().AddToNamed("Titles"); + ws.Cell(2, 3).InsertData(listOfArr); - // From a DataTable - var dataTable = GetTable(); - ws.Cell(6, 1).Value = "From DataTable"; - ws.Range(6, 1, 6, 4).Merge().AddToNamed("Titles"); - ws.Cell(7, 1).InsertData(dataTable); + // From a DataTable + var dataTable = GetTable(); + ws.Cell(6, 1).Value = "From DataTable"; + ws.Range(6, 1, 6, 4).Merge().AddToNamed("Titles"); + ws.Cell(7, 1).InsertData(dataTable); - // From a query - var list = new List(); - list.Add(new Person() { Name = "John", Age = 30, House = "On Elm St." }); - list.Add(new Person() { Name = "Mary", Age = 15, House = "On Main St." }); - list.Add(new Person() { Name = "Luis", Age = 21, House = "On 23rd St." }); - list.Add(new Person() { Name = "Henry", Age = 45, House = "On 5th Ave." }); + // From a query + var list = new List(); + list.Add(new Person() { Name = "John", Age = 30, House = "On Elm St." }); + list.Add(new Person() { Name = "Mary", Age = 15, House = "On Main St." }); + list.Add(new Person() { Name = "Luis", Age = 21, House = "On 23rd St." }); + list.Add(new Person() { Name = "Henry", Age = 45, House = "On 5th Ave." }); - var people = from p in list - where p.Age >= 21 - select new { p.Name, p.House, p.Age }; + var people = from p in list + where p.Age >= 21 + select new { p.Name, p.House, p.Age }; - ws.Cell(6, 6).Value = "From Query"; - ws.Range(6, 6, 6, 8).Merge().AddToNamed("Titles"); - ws.Cell(7, 6).InsertData(people); + ws.Cell(6, 6).Value = "From Query"; + ws.Range(6, 6, 6, 8).Merge().AddToNamed("Titles"); + ws.Cell(7, 6).InsertData(people); - ws.Cell("F13").Value = "Transposed"; - ws.Range(13, 6, 13, 8).Merge().AddToNamed("Titles"); - ws.Cell("F14").InsertData(people.AsEnumerable(), true); + ws.Cell(11, 6).Value = "From List"; + ws.Range(11, 6, 11, 9).Merge().AddToNamed("Titles"); + ws.Cell(12, 6).InsertData(list); - // Prepare the style for the titles - var titlesStyle = wb.Style; - titlesStyle.Font.Bold = true; - titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; - titlesStyle.Fill.BackgroundColor = XLColor.Cyan; + ws.Cell("A13").Value = "Transposed"; + ws.Range(13, 1, 13, 3).Merge().AddToNamed("Titles"); + ws.Cell("A14").InsertData(people.AsEnumerable(), true); - // Format all titles in one shot - wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle; + // Prepare the style for the titles + var titlesStyle = wb.Style; + titlesStyle.Font.Bold = true; + titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; + titlesStyle.Fill.BackgroundColor = XLColor.Cyan; - ws.Columns().AdjustToContents(); + // Format all titles in one shot + wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle; - wb.SaveAs(filePath); - } + ws.Columns().AdjustToContents(); + + wb.SaveAs(filePath); + } } - class Person + private class Person { public String House { get; set; } public String Name { get; set; } public Int32 Age { get; set; } + public static String ClassType { get { return nameof(Person); } } } // Private private DataTable GetTable() { - DataTable table = new DataTable(); table.Columns.Add("Dosage", typeof(int)); table.Columns.Add("Drug", typeof(string)); @@ -98,9 +102,9 @@ table.Rows.Add(100, "Dilantin", "Melanie", new DateTime(2000, 1, 5)); return table; } + // Override - - #endregion + #endregion Methods } } diff --git a/ClosedXML_Examples/Misc/InsertingTables.cs b/ClosedXML_Examples/Misc/InsertingTables.cs deleted file mode 100644 index fb6a3a0..0000000 --- a/ClosedXML_Examples/Misc/InsertingTables.cs +++ /dev/null @@ -1,102 +0,0 @@ -using ClosedXML.Attributes; -using ClosedXML.Excel; -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; - -namespace ClosedXML_Examples.Misc -{ - public class InsertingTables : IXLExample - { - #region Methods - - // Public - public void Create(String filePath) - { - var wb = new XLWorkbook(); - var ws = wb.Worksheets.Add("Inserting Tables"); - - // From a list of strings - var listOfStrings = new List(); - listOfStrings.Add("House"); - listOfStrings.Add("Car"); - ws.Cell(1, 1).Value = "From Strings"; - ws.Cell(1, 1).AsRange().AddToNamed("Titles"); - ws.Cell(2, 1).InsertTable(listOfStrings); - - // From a list of arrays - var listOfArr = new List(); - listOfArr.Add(new Int32[] { 1, 2, 3 }); - listOfArr.Add(new Int32[] { 1 }); - listOfArr.Add(new Int32[] { 1, 2, 3, 4, 5, 6 }); - ws.Cell(1, 3).Value = "From Arrays"; - ws.Range(1, 3, 1, 8).Merge().AddToNamed("Titles"); - ws.Cell(2, 3).InsertTable(listOfArr); - - // From a DataTable - var dataTable = GetTable(); - ws.Cell(7, 1).Value = "From DataTable"; - ws.Range(7, 1, 7, 4).Merge().AddToNamed("Titles"); - ws.Cell(8, 1).InsertTable(dataTable); - - // From a query - var list = new List(); - list.Add(new Person() { Name = "John", Age = 30, House = "On Elm St." }); - list.Add(new Person() { Name = "Mary", Age = 15, House = "On Main St." }); - list.Add(new Person() { Name = "Luis", Age = 21, House = "On 23rd St." }); - list.Add(new Person() { Name = "Henry", Age = 45, House = "On 5th Ave." }); - - var people = from p in list - where p.Age >= 21 - select p; - - ws.Cell(7, 6).Value = "From Query"; - ws.Range(7, 6, 7, 8).Merge().AddToNamed("Titles"); - ws.Cell(8, 6).InsertTable(people); - - // Prepare the style for the titles - var titlesStyle = wb.Style; - titlesStyle.Font.Bold = true; - titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; - titlesStyle.Fill.BackgroundColor = XLColor.Cyan; - - // Format all titles in one shot - wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle; - - ws.Columns().AdjustToContents(); - - wb.SaveAs(filePath); - } - - private class Person - { - [XLColumn(Header = "House Street")] - public String House { get; set; } - - public String Name { get; set; } - public Int32 Age { get; set; } - } - - // Private - private DataTable GetTable() - { - DataTable table = new DataTable(); - table.Columns.Add("Dosage", typeof(int)); - table.Columns.Add("Drug", typeof(string)); - table.Columns.Add("Patient", typeof(string)); - table.Columns.Add("Date", typeof(DateTime)); - - table.Rows.Add(25, "Indocin", "David", new DateTime(2000, 1, 1)); - table.Rows.Add(50, "Enebrel", "Sam", new DateTime(2000, 1, 2)); - table.Rows.Add(10, "Hydralazine", "Christoff", new DateTime(2000, 1, 3)); - table.Rows.Add(21, "Combivent", "Janet", new DateTime(2000, 1, 4)); - table.Rows.Add(100, "Dilantin", "Melanie", new DateTime(2000, 1, 5)); - return table; - } - - // Override - - #endregion Methods - } -} diff --git a/ClosedXML_Examples/Ranges/UsingTables.cs b/ClosedXML_Examples/Ranges/UsingTables.cs deleted file mode 100644 index e5c24b2..0000000 --- a/ClosedXML_Examples/Ranges/UsingTables.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.IO; -using ClosedXML.Excel; -using System.Linq; - - -namespace ClosedXML_Examples.Ranges -{ - public class UsingTables : IXLExample - { - #region Methods - - // Public - public void Create(String filePath) - { - string tempFile = ExampleHelper.GetTempFilePath(filePath); - try - { - new BasicTable().Create(tempFile); - var wb = new XLWorkbook(tempFile); - var ws = wb.Worksheet(1); - ws.Name = "Contacts Table"; - var firstCell = ws.FirstCellUsed(); - var lastCell = ws.LastCellUsed(); - var range = ws.Range(firstCell.Address, lastCell.Address); - range.FirstRow().Delete(); // Deleting the "Contacts" header (we don't need it for our purposes) - - // We want to use a theme for table, not the hard coded format of the BasicTable - range.Clear(XLClearOptions.Formats); - // Put back the date and number formats - range.Column(4).Style.NumberFormat.NumberFormatId = 15; - range.Column(5).Style.NumberFormat.Format = "$ #,##0"; - - var table = range.CreateTable(); // You can also use range.AsTable() if you want to - // manipulate the range as a table but don't want - // to create the table in the worksheet. - - // Let's activate the Totals row and add the sum of Income - table.ShowTotalsRow = true; - table.Field("Income").TotalsRowFunction = XLTotalsRowFunction.Sum; - // Just for fun let's add the text "Sum Of Income" to the totals row - table.Field(0).TotalsRowLabel = "Sum Of Income"; - - // Copy all the headers - Int32 columnWithHeaders = lastCell.Address.ColumnNumber + 2; - Int32 currentRow = table.RangeAddress.FirstAddress.RowNumber; - ws.Cell(currentRow, columnWithHeaders).Value = "Table Headers"; - foreach (var cell in table.HeadersRow().Cells()) - { - currentRow++; - ws.Cell(currentRow, columnWithHeaders).Value = cell.Value; - } - - // Format the headers as a table with a different style and no autofilters - var htFirstCell = ws.Cell(table.RangeAddress.FirstAddress.RowNumber, columnWithHeaders); - var htLastCell = ws.Cell(currentRow, columnWithHeaders); - var headersTable = ws.Range(htFirstCell, htLastCell).CreateTable("Headers"); - headersTable.Theme = XLTableTheme.TableStyleLight10; - headersTable.ShowAutoFilter = false; - - // Add a custom formula to the headersTable - headersTable.ShowTotalsRow = true; - headersTable.Field(0).TotalsRowFormulaA1 = "CONCATENATE(\"Count: \", CountA(Headers[Table Headers]))"; - - // Copy the names - Int32 columnWithNames = columnWithHeaders + 2; - currentRow = table.RangeAddress.FirstAddress.RowNumber; // reset the currentRow - ws.Cell(currentRow, columnWithNames).Value = "Names"; - foreach (var row in table.DataRange.Rows()) - { - currentRow++; - var fName = row.Field("FName").GetString(); // Notice how we're calling the cell by field name - var lName = row.Field("LName").GetString(); // Notice how we're calling the cell by field name - var name = String.Format("{0} {1}", fName, lName); - ws.Cell(currentRow, columnWithNames).Value = name; - } - - // Format the names as a table with a different style and no autofilters - var ntFirstCell = ws.Cell(table.RangeAddress.FirstAddress.RowNumber, columnWithNames); - var ntLastCell = ws.Cell(currentRow, columnWithNames); - var namesTable = ws.Range(ntFirstCell, ntLastCell).CreateTable(); - namesTable.Theme = XLTableTheme.TableStyleLight12; - namesTable.ShowAutoFilter = false; - - ws.Columns().AdjustToContents(); - ws.Columns("A,G,I").Width = 3; - - wb.SaveAs(filePath); - } - finally - { - if (File.Exists(tempFile)) - { - File.Delete(tempFile); - } - } - } - - // Private - - // Override - - - #endregion - } -} diff --git a/ClosedXML_Examples/Styles/UsingPhonetics.cs b/ClosedXML_Examples/Styles/UsingPhonetics.cs index eaf2e32..f6c6483 100644 --- a/ClosedXML_Examples/Styles/UsingPhonetics.cs +++ b/ClosedXML_Examples/Styles/UsingPhonetics.cs @@ -23,8 +23,10 @@ // And then we add the phonetics cell.RichText.Phonetics.SetFontSize(8); - cell.RichText.Phonetics.Add("げん", 7, 1); - cell.RichText.Phonetics.Add("き", 8, 1); + cell.RichText.Phonetics.Add("げん", 7, 8); + cell.RichText.Phonetics.Add("き", 8, 9); + + //TODO: I'm looking for someone who understands Japanese to confirm the validity of the above code. wb.SaveAs(filePath); } diff --git a/ClosedXML_Examples/Tables/InsertingTables.cs b/ClosedXML_Examples/Tables/InsertingTables.cs new file mode 100644 index 0000000..176eed1 --- /dev/null +++ b/ClosedXML_Examples/Tables/InsertingTables.cs @@ -0,0 +1,111 @@ +using ClosedXML.Attributes; +using ClosedXML.Excel; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; + +namespace ClosedXML_Examples.Tables +{ + public class InsertingTables : IXLExample + { + #region Methods + + // Public + public void Create(String filePath) + { + using (var wb = new XLWorkbook()) + { + var ws = wb.Worksheets.Add("Inserting Tables"); + + // From a list of strings + var listOfStrings = new List(); + listOfStrings.Add("House"); + listOfStrings.Add("Car"); + ws.Cell(1, 1).Value = "From Strings"; + ws.Cell(1, 1).AsRange().AddToNamed("Titles"); + ws.Cell(2, 1).InsertTable(listOfStrings); + + // From a list of arrays + var listOfArr = new List(); + listOfArr.Add(new Int32[] { 1, 2, 3 }); + listOfArr.Add(new Int32[] { 1 }); + listOfArr.Add(new Int32[] { 1, 2, 3, 4, 5, 6 }); + ws.Cell(1, 3).Value = "From Arrays"; + ws.Range(1, 3, 1, 8).Merge().AddToNamed("Titles"); + ws.Cell(2, 3).InsertTable(listOfArr); + + // From a DataTable + var dataTable = GetTable(); + ws.Cell(7, 1).Value = "From DataTable"; + ws.Range(7, 1, 7, 4).Merge().AddToNamed("Titles"); + ws.Cell(8, 1).InsertTable(dataTable); + + // From a query + var list = new List(); + list.Add(new Person() { Name = "John", Age = 30, House = "On Elm St." }); + list.Add(new Person() { Name = "Mary", Age = 15, House = "On Main St." }); + list.Add(new Person() { Name = "Luis", Age = 21, House = "On 23rd St." }); + list.Add(new Person() { Name = "Henry", Age = 45, House = "On 5th Ave." }); + + var people = from p in list + where p.Age >= 21 + select p; + + ws.Cell(7, 6).Value = "From Query"; + ws.Range(7, 6, 7, 9).Merge().AddToNamed("Titles"); + ws.Cell(8, 6).InsertTable(people); + + ws.Cell(15, 6).Value = "From List"; + ws.Range(15, 6, 15, 9).Merge().AddToNamed("Titles"); + ws.Cell(16, 6).InsertTable(people); + + // Prepare the style for the titles + var titlesStyle = wb.Style; + titlesStyle.Font.Bold = true; + titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; + titlesStyle.Fill.BackgroundColor = XLColor.Cyan; + + // Format all titles in one shot + wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle; + + ws.Columns().AdjustToContents(); + + wb.SaveAs(filePath); + } + } + + private class Person + { + [XLColumn(Header = "House Street")] + public String House { get; set; } + + public String Name { get; set; } + public Int32 Age { get; set; } + + [XLColumn(Header = "Class Type")] + public static String ClassType { get { return nameof(Person); } } + } + + // Private + private DataTable GetTable() + { + DataTable table = new DataTable(); + table.Columns.Add("Dosage", typeof(int)); + table.Columns.Add("Drug", typeof(string)); + table.Columns.Add("Patient", typeof(string)); + table.Columns.Add("Date", typeof(DateTime)); + + table.Rows.Add(25, "Indocin", "David", new DateTime(2000, 1, 1)); + table.Rows.Add(50, "Enebrel", "Sam", new DateTime(2000, 1, 2)); + table.Rows.Add(10, "Hydralazine", "Christoff", new DateTime(2000, 1, 3)); + table.Rows.Add(21, "Combivent", "Janet", new DateTime(2000, 1, 4)); + table.Rows.Add(100, "Dilantin", "Melanie", new DateTime(2000, 1, 5)); + return table; + } + + // Override + + #endregion Methods + } +} diff --git a/ClosedXML_Examples/Tables/ResizingTables.cs b/ClosedXML_Examples/Tables/ResizingTables.cs new file mode 100644 index 0000000..64ad34e --- /dev/null +++ b/ClosedXML_Examples/Tables/ResizingTables.cs @@ -0,0 +1,47 @@ +using ClosedXML.Excel; +using System; +using System.Linq; + +// TODO: Add example to Wiki + +namespace ClosedXML_Examples.Tables +{ + public class ResizingTables : IXLExample + { + public void Create(string filePath) + { + using (var wb = new XLWorkbook()) + { + var ws1 = wb.AddWorksheet("Sheet1"); + + var data1 = Enumerable.Range(1, 10) + .Select(i => + new + { + Index = i, + Character = Convert.ToChar(64 + i), + String = new String('a', i), + Integer = 64 + i + }); + + var table1 = ws1.Cell("B2").InsertTable(data1, true) + .SetShowHeaderRow() + .SetShowTotalsRow(); + + table1.Fields.First().TotalsRowLabel = "Sum of Integer"; + table1.Fields.Last().TotalsRowFunction = XLTotalsRowFunction.Sum; + + var ws2 = ws1.CopyTo("Sheet2"); + var table2 = ws2.Tables.First(); + table2.Resize(table2.FirstCell(), table2.LastCell().CellLeft().CellAbove(3)); + + var ws3 = ws2.CopyTo("Sheet3"); + var table3 = ws3.Tables.First(); + table3.Resize(table3.FirstCell().CellLeft(), table3.LastCell().CellRight().CellBelow(1)); + + wb.Worksheets.ForEach(ws => ws.Columns().AdjustToContents()); + wb.SaveAs(filePath); + } + } + } +} diff --git a/ClosedXML_Examples/Tables/UsingTables.cs b/ClosedXML_Examples/Tables/UsingTables.cs new file mode 100644 index 0000000..639a0a0 --- /dev/null +++ b/ClosedXML_Examples/Tables/UsingTables.cs @@ -0,0 +1,105 @@ +using ClosedXML.Excel; +using System; +using System.IO; + +namespace ClosedXML_Examples.Tables +{ + public class UsingTables : IXLExample + { + #region Methods + + // Public + public void Create(String filePath) + { + string tempFile = ExampleHelper.GetTempFilePath(filePath); + try + { + new BasicTable().Create(tempFile); + using (var wb = new XLWorkbook(tempFile)) + { + var ws = wb.Worksheet(1); + ws.Name = "Contacts Table"; + var firstCell = ws.FirstCellUsed(); + var lastCell = ws.LastCellUsed(); + var range = ws.Range(firstCell.Address, lastCell.Address); + range.FirstRow().Delete(); // Deleting the "Contacts" header (we don't need it for our purposes) + + // We want to use a theme for table, not the hard coded format of the BasicTable + range.Clear(XLClearOptions.Formats); + // Put back the date and number formats + range.Column(4).Style.NumberFormat.NumberFormatId = 15; + range.Column(5).Style.NumberFormat.Format = "$ #,##0"; + + var table = range.CreateTable(); // You can also use range.AsTable() if you want to + // manipulate the range as a table but don't want + // to create the table in the worksheet. + + // Let's activate the Totals row and add the sum of Income + table.ShowTotalsRow = true; + table.Field("Income").TotalsRowFunction = XLTotalsRowFunction.Sum; + // Just for fun let's add the text "Sum Of Income" to the totals row + table.Field(0).TotalsRowLabel = "Sum Of Income"; + + // Copy all the headers + Int32 columnWithHeaders = lastCell.Address.ColumnNumber + 2; + Int32 currentRow = table.RangeAddress.FirstAddress.RowNumber; + ws.Cell(currentRow, columnWithHeaders).Value = "Table Headers"; + foreach (var cell in table.HeadersRow().Cells()) + { + currentRow++; + ws.Cell(currentRow, columnWithHeaders).Value = cell.Value; + } + + // Format the headers as a table with a different style and no autofilters + var htFirstCell = ws.Cell(table.RangeAddress.FirstAddress.RowNumber, columnWithHeaders); + var htLastCell = ws.Cell(currentRow, columnWithHeaders); + var headersTable = ws.Range(htFirstCell, htLastCell).CreateTable("Headers"); + headersTable.Theme = XLTableTheme.TableStyleLight10; + headersTable.ShowAutoFilter = false; + + // Add a custom formula to the headersTable + headersTable.ShowTotalsRow = true; + headersTable.Field(0).TotalsRowFormulaA1 = "CONCATENATE(\"Count: \", CountA(Headers[Table Headers]))"; + + // Copy the names + Int32 columnWithNames = columnWithHeaders + 2; + currentRow = table.RangeAddress.FirstAddress.RowNumber; // reset the currentRow + ws.Cell(currentRow, columnWithNames).Value = "Names"; + foreach (var row in table.DataRange.Rows()) + { + currentRow++; + var fName = row.Field("FName").GetString(); // Notice how we're calling the cell by field name + var lName = row.Field("LName").GetString(); // Notice how we're calling the cell by field name + var name = String.Format("{0} {1}", fName, lName); + ws.Cell(currentRow, columnWithNames).Value = name; + } + + // Format the names as a table with a different style and no autofilters + var ntFirstCell = ws.Cell(table.RangeAddress.FirstAddress.RowNumber, columnWithNames); + var ntLastCell = ws.Cell(currentRow, columnWithNames); + var namesTable = ws.Range(ntFirstCell, ntLastCell).CreateTable(); + namesTable.Theme = XLTableTheme.TableStyleLight12; + namesTable.ShowAutoFilter = false; + + ws.Columns().AdjustToContents(); + ws.Columns("A,G,I").Width = 3; + + wb.SaveAs(filePath); + } + } + finally + { + if (File.Exists(tempFile)) + { + File.Delete(tempFile); + } + } + } + + // Private + + // Override + + #endregion Methods + } +} diff --git a/ClosedXML_Tests/Examples/MiscTests.cs b/ClosedXML_Tests/Examples/MiscTests.cs index 4f14453..d74a9fc 100644 --- a/ClosedXML_Tests/Examples/MiscTests.cs +++ b/ClosedXML_Tests/Examples/MiscTests.cs @@ -133,11 +133,6 @@ TestHelper.RunTestExample(@"Misc\InsertingData.xlsx"); } - [Test] - public void InsertingTables() - { - TestHelper.RunTestExample(@"Misc\InsertingTables.xlsx"); - } [Test] public void LambdaExpressions() @@ -204,11 +199,11 @@ { TestHelper.RunTestExample(@"Misc\WorkbookProperties.xlsx"); } - + [Test] public void WorkbookProtection() { TestHelper.RunTestExample(@"Misc\WorkbookProtection.xlsx"); } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/Examples/RangesTests.cs b/ClosedXML_Tests/Examples/RangesTests.cs index fe3e1e2..59cd62b 100644 --- a/ClosedXML_Tests/Examples/RangesTests.cs +++ b/ClosedXML_Tests/Examples/RangesTests.cs @@ -99,12 +99,6 @@ } [Test] - public void UsingTables() - { - TestHelper.RunTestExample(@"Ranges\UsingTables.xlsx"); - } - - [Test] public void AddingRowToTables() { TestHelper.RunTestExample(@"Ranges\AddingRowToTables.xlsx"); @@ -116,4 +110,4 @@ TestHelper.RunTestExample(@"Ranges\WalkingRanges.xlsx"); } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/Examples/StylesTests.cs b/ClosedXML_Tests/Examples/StylesTests.cs index 4ecfb15..c400a81 100644 --- a/ClosedXML_Tests/Examples/StylesTests.cs +++ b/ClosedXML_Tests/Examples/StylesTests.cs @@ -67,9 +67,15 @@ } [Test] + public void UsingPhonetics() + { + TestHelper.RunTestExample(@"Styles\UsingPhonetics.xlsx"); + } + + [Test] public void UsingRichText() { TestHelper.RunTestExample(@"Styles\UsingRichText.xlsx"); } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/Examples/TablesTests.cs b/ClosedXML_Tests/Examples/TablesTests.cs new file mode 100644 index 0000000..934bf65 --- /dev/null +++ b/ClosedXML_Tests/Examples/TablesTests.cs @@ -0,0 +1,27 @@ +using ClosedXML_Examples.Tables; +using NUnit.Framework; + +namespace ClosedXML_Tests.Examples +{ + [TestFixture] + public class TablesTests + { + [Test] + public void InsertingTables() + { + TestHelper.RunTestExample(@"Tables\InsertingTables.xlsx"); + } + + [Test] + public void ResizingTables() + { + TestHelper.RunTestExample(@"Tables\ResizingTables.xlsx"); + } + + [Test] + public void UsingTables() + { + TestHelper.RunTestExample(@"Tables\UsingTables.xlsx"); + } + } +} diff --git a/ClosedXML_Tests/Excel/CalcEngine/CalcEngineExceptionTests.cs b/ClosedXML_Tests/Excel/CalcEngine/CalcEngineExceptionTests.cs new file mode 100644 index 0000000..b1be35f --- /dev/null +++ b/ClosedXML_Tests/Excel/CalcEngine/CalcEngineExceptionTests.cs @@ -0,0 +1,29 @@ +using ClosedXML.Excel; +using ClosedXML.Excel.CalcEngine.Exceptions; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; + +namespace ClosedXML_Tests.Excel.CalcEngine +{ + [TestFixture] + public class CalcEngineExceptionTests + { + [OneTimeSetUp] + public void SetCultureInfo() + { + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); + } + + [Test] + public void InvalidCharNumber() + { + Assert.Throws(() => XLWorkbook.EvaluateExpr("CHAR(-2)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr("CHAR(270)")); + } + } +} diff --git a/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs b/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs index 5992793..07b0027 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs @@ -158,6 +158,17 @@ } #endregion IsLogical Tests + [Test] + public void IsNA() + { + object actual; + actual = XLWorkbook.EvaluateExpr("ISNA(#N/A)"); + Assert.AreEqual(true, actual); + + actual = XLWorkbook.EvaluateExpr("ISNA(#REF!)"); + Assert.AreEqual(false, actual); + } + #region IsNotText Tests [Test] @@ -288,6 +299,30 @@ } #endregion IsOdd Test + [Test] + public void IsRef() + { + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet"); + ws.Cell("A1").Value = "123"; + + ws.Cell("B1").FormulaA1 = "ISREF(A1)"; + ws.Cell("B2").FormulaA1 = "ISREF(5)"; + ws.Cell("B3").FormulaA1 = "ISREF(YEAR(TODAY()))"; + + bool actual; + actual = ws.Cell("B1").GetValue(); + Assert.AreEqual(true, actual); + + actual = ws.Cell("B2").GetValue(); + Assert.AreEqual(false, actual); + + actual = ws.Cell("B3").GetValue(); + Assert.AreEqual(false, actual); + } + } + #region IsText Tests [Test] diff --git a/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs b/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs index dfc0db1..82a82ed 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs @@ -1,4 +1,5 @@ using ClosedXML.Excel; +using ClosedXML.Excel.CalcEngine.Exceptions; using NUnit.Framework; using System; @@ -122,11 +123,11 @@ [Test] public void Vlookup_Exceptions() { - Assert.That(() => workbook.Evaluate(@"=VLOOKUP("""",Data!$B$2:$I$71,3,FALSE)"), Throws.TypeOf()); - Assert.That(() => workbook.Evaluate(@"=VLOOKUP(50,Data!$B$2:$I$71,3,FALSE)"), Throws.TypeOf()); - Assert.That(() => workbook.Evaluate(@"=VLOOKUP(20,Data!$B$2:$I$71,9,FALSE)"), Throws.TypeOf()); + Assert.Throws(() => workbook.Evaluate(@"=VLOOKUP("""",Data!$B$2:$I$71,3,FALSE)")); + Assert.Throws(() => workbook.Evaluate(@"=VLOOKUP(50,Data!$B$2:$I$71,3,FALSE)")); + Assert.Throws(() => workbook.Evaluate(@"=VLOOKUP(-1,Data!$B$2:$I$71,2,TRUE)")); - Assert.That(() => workbook.Evaluate(@"=VLOOKUP(-1,Data!$B$2:$I$71,9,TRUE)"), Throws.TypeOf()); + Assert.Throws(() => workbook.Evaluate(@"=VLOOKUP(20,Data!$B$2:$I$71,9,FALSE)")); } } } diff --git a/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs b/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs index 03fe728..8511a5f 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs @@ -1,5 +1,6 @@ using ClosedXML.Excel; using ClosedXML.Excel.CalcEngine; +using ClosedXML.Excel.CalcEngine.Exceptions; using NUnit.Framework; using System; using System.Globalization; @@ -20,13 +21,13 @@ [Test] public void Char_Empty_Input_String() { - Assert.That(() => XLWorkbook.EvaluateExpr(@"Char("""")"), Throws.TypeOf()); + Assert.Throws(() => XLWorkbook.EvaluateExpr(@"Char("""")")); } [Test] public void Char_Input_Too_Large() { - Assert.That(() => XLWorkbook.EvaluateExpr(@"Char(9797)"), Throws.TypeOf()); + Assert.Throws< CellValueException>(() => XLWorkbook.EvaluateExpr(@"Char(9797)")); } [Test] diff --git a/ClosedXML_Tests/Excel/Coordinates/XLAddressTests.cs b/ClosedXML_Tests/Excel/Coordinates/XLAddressTests.cs index 4d0329f..686c1f4 100644 --- a/ClosedXML_Tests/Excel/Coordinates/XLAddressTests.cs +++ b/ClosedXML_Tests/Excel/Coordinates/XLAddressTests.cs @@ -9,13 +9,14 @@ [Test] public void ToStringTest() { - IXLWorksheet ws = new XLWorkbook().Worksheets.Add("Sheet1"); + var ws = new XLWorkbook().Worksheets.Add("Sheet1"); IXLAddress address = ws.Cell(1, 1).Address; Assert.AreEqual("A1", address.ToString()); Assert.AreEqual("A1", address.ToString(XLReferenceStyle.A1)); Assert.AreEqual("R1C1", address.ToString(XLReferenceStyle.R1C1)); Assert.AreEqual("A1", address.ToString(XLReferenceStyle.Default)); + Assert.AreEqual("Sheet1!A1", address.ToString(XLReferenceStyle.Default, true)); Assert.AreEqual("A1", address.ToStringRelative()); Assert.AreEqual("Sheet1!A1", address.ToStringRelative(true)); @@ -39,6 +40,7 @@ Assert.AreEqual("A1", address.ToString(XLReferenceStyle.A1)); Assert.AreEqual("R1C1", address.ToString(XLReferenceStyle.R1C1)); Assert.AreEqual("A1", address.ToString(XLReferenceStyle.Default)); + Assert.AreEqual("'Sheet 1'!A1", address.ToString(XLReferenceStyle.Default, true)); Assert.AreEqual("A1", address.ToStringRelative()); Assert.AreEqual("'Sheet 1'!A1", address.ToStringRelative(true)); diff --git a/ClosedXML_Tests/Excel/Misc/FormulaTests.cs b/ClosedXML_Tests/Excel/Misc/FormulaTests.cs index 16ac2a1..3ff4c55 100644 --- a/ClosedXML_Tests/Excel/Misc/FormulaTests.cs +++ b/ClosedXML_Tests/Excel/Misc/FormulaTests.cs @@ -1,4 +1,5 @@ using ClosedXML.Excel; +using ClosedXML.Excel.CalcEngine.Exceptions; using NUnit.Framework; using System; using System.Linq; @@ -173,5 +174,17 @@ actual = XLWorkbook.EvaluateExpr("+MID(\"This is a test\", 6, 2)"); Assert.AreEqual("is", actual); } + + [Test] + public void FormulasWithErrors() + { + Assert.Throws(() => XLWorkbook.EvaluateExpr("YEAR(#REF!)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr("YEAR(#VALUE!)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr("YEAR(#DIV/0!)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr("YEAR(#NAME?)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr("YEAR(#N/A)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr("YEAR(#NULL!)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr("YEAR(#NUM!)")); + } } } diff --git a/ClosedXML_Tests/Excel/Misc/HyperlinkTests.cs b/ClosedXML_Tests/Excel/Misc/HyperlinkTests.cs new file mode 100644 index 0000000..95b2431 --- /dev/null +++ b/ClosedXML_Tests/Excel/Misc/HyperlinkTests.cs @@ -0,0 +1,32 @@ +using ClosedXML.Excel; +using NUnit.Framework; + +namespace ClosedXML_Tests.Excel.Misc +{ + [TestFixture] + public class HyperlinkTests + { + [Test] + public void TestHyperlinks() + { + using (var wb = new XLWorkbook()) + { + var ws1 = wb.Worksheets.Add("Sheet1"); + var ws2 = wb.Worksheets.Add("Sheet2"); + + var targetCell = ws2.Cell("A1"); + var targetRange = ws2.Range("A1", "B1"); + + var linkCell1 = ws1.Cell("A1"); + linkCell1.Value = "Link to IXLCell"; + linkCell1.Hyperlink = new XLHyperlink(targetCell); + Assert.AreEqual("Sheet2!A1", linkCell1.Hyperlink.InternalAddress); + + var linkRange1 = ws1.Cell("A2"); + linkRange1.Value = "Link to IXLRangeBase"; + linkRange1.Hyperlink = new XLHyperlink(targetRange); + Assert.AreEqual("Sheet2!A1:B1", linkRange1.Hyperlink.InternalAddress); + } + } + } +} diff --git a/ClosedXML_Tests/Excel/Ranges/XLRangeAddressTests.cs b/ClosedXML_Tests/Excel/Ranges/XLRangeAddressTests.cs index 669e811..c103c6e 100644 --- a/ClosedXML_Tests/Excel/Ranges/XLRangeAddressTests.cs +++ b/ClosedXML_Tests/Excel/Ranges/XLRangeAddressTests.cs @@ -13,6 +13,7 @@ IXLRangeAddress address = ws.Cell(1, 1).AsRange().RangeAddress; Assert.AreEqual("A1:A1", address.ToString()); + Assert.AreEqual("Sheet1!R1C1:R1C1", address.ToString(XLReferenceStyle.R1C1, true)); Assert.AreEqual("A1:A1", address.ToStringRelative()); Assert.AreEqual("Sheet1!A1:A1", address.ToStringRelative(true)); @@ -33,6 +34,7 @@ IXLRangeAddress address = ws.Cell(1, 1).AsRange().RangeAddress; Assert.AreEqual("A1:A1", address.ToString()); + Assert.AreEqual("'Sheet 1'!R1C1:R1C1", address.ToString(XLReferenceStyle.R1C1, true)); Assert.AreEqual("A1:A1", address.ToStringRelative()); Assert.AreEqual("'Sheet 1'!A1:A1", address.ToStringRelative(true)); diff --git a/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs b/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs index 191bdfc..7847ae5 100644 --- a/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs +++ b/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs @@ -2,7 +2,6 @@ using NUnit.Framework; using System; using System.Data; -using System.Linq; namespace ClosedXML_Tests.Excel { diff --git a/ClosedXML_Tests/Excel/Tables/TablesTests.cs b/ClosedXML_Tests/Excel/Tables/TablesTests.cs index b114d36..1378f90 100644 --- a/ClosedXML_Tests/Excel/Tables/TablesTests.cs +++ b/ClosedXML_Tests/Excel/Tables/TablesTests.cs @@ -133,7 +133,7 @@ DataRow dr = dt.NewRow(); dr[columnName] = "some text"; dt.Rows.Add(dr); - ws.Cell(1, 1).InsertTable(dt); + ws.Cell(1, 1).InsertTable(dt); IXLTable table1 = ws.Table(0); string fieldName1 = table1.Field(0).Name; @@ -451,5 +451,134 @@ Assert.AreEqual(2, table.Fields.Last().Index); } } + + [Test] + public void OverlappingTablesThrowsException() + { + var dt = new DataTable("sheet1"); + dt.Columns.Add("col1", typeof(string)); + dt.Columns.Add("col2", typeof(double)); + + using (var wb = new XLWorkbook()) + { + IXLWorksheet ws = wb.AddWorksheet("Sheet1"); + ws.FirstCell().InsertTable(dt, true); + Assert.Throws(() => ws.FirstCell().CellRight().InsertTable(dt, true)); + } +} + + [Test] + public void OverwritingTableTotalsRow() + { + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + + var data1 = Enumerable.Range(1, 10) + .Select(i => + new + { + Index = i, + Character = Convert.ToChar(64 + i), + String = new String('a', i) + }); + + var table = ws.FirstCell().InsertTable(data1, true) + .SetShowHeaderRow() + .SetShowTotalsRow(); + table.Fields.First().TotalsRowFunction = XLTotalsRowFunction.Sum; + + var data2 = Enumerable.Range(1, 20) + .Select(i => + new + { + Index = i, + Character = Convert.ToChar(64 + i), + String = new String('b', i), + Int = 64 + i + }); + + ws.FirstCell().CellBelow().InsertData(data2); + + table.Fields.ForEach(f => Assert.AreEqual(XLTotalsRowFunction.None, f.TotalsRowFunction)); + + Assert.AreEqual("11", table.Field(0).TotalsRowLabel); + Assert.AreEqual("K", table.Field(1).TotalsRowLabel); + Assert.AreEqual("bbbbbbbbbbb", table.Field(2).TotalsRowLabel); + } + } + + [Test] + public void CanResizeTable() + { + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + + var data1 = Enumerable.Range(1, 10) + .Select(i => + new + { + Index = i, + Character = Convert.ToChar(64 + i), + String = new String('a', i) + }); + + var table = ws.FirstCell().InsertTable(data1, true) + .SetShowHeaderRow() + .SetShowTotalsRow(); + table.Fields.First().TotalsRowFunction = XLTotalsRowFunction.Sum; + + var data2 = Enumerable.Range(1, 10) + .Select(i => + new + { + Index = i, + Character = Convert.ToChar(64 + i), + String = new String('b', i), + Integer = 64 + i + }); + + ws.FirstCell().CellBelow().InsertData(data2); + table.Resize(table.FirstCell().Address, table.AsRange().LastCell().CellRight().Address); + + Assert.AreEqual(4, table.Fields.Count()); + + Assert.AreEqual("Column4", table.Field(3).Name); + + ws.Cell("D1").Value = "Integer"; + Assert.AreEqual("Integer", table.Field(3).Name); + } + } + + [Test] + public void TestTableCellTypes() + { + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + + var data1 = Enumerable.Range(1, 10) + .Select(i => + new + { + Index = i, + Character = Convert.ToChar(64 + i), + String = new String('a', i) + }); + + var table = ws.FirstCell().InsertTable(data1, true) + .SetShowHeaderRow() + .SetShowTotalsRow(); + table.Fields.First().TotalsRowFunction = XLTotalsRowFunction.Sum; + + Assert.AreEqual(XLTableCellType.Header, table.HeadersRow().Cell(1).TableCellType()); + Assert.AreEqual(XLTableCellType.Data, table.HeadersRow().Cell(1).CellBelow().TableCellType()); + Assert.AreEqual(XLTableCellType.Total, table.TotalsRow().Cell(1).TableCellType()); + Assert.AreEqual(XLTableCellType.None, ws.Cell("Z100").TableCellType()); + } + } + + //TODO: Delete table (not underlying range) } } diff --git a/ClosedXML_Tests/Excel/Worksheets/XLWorksheetTests.cs b/ClosedXML_Tests/Excel/Worksheets/XLWorksheetTests.cs index 0918e5a..76f612e 100644 --- a/ClosedXML_Tests/Excel/Worksheets/XLWorksheetTests.cs +++ b/ClosedXML_Tests/Excel/Worksheets/XLWorksheetTests.cs @@ -1,6 +1,7 @@ using ClosedXML.Excel; using NUnit.Framework; using System; +using System.IO; using System.Linq; namespace ClosedXML_Tests @@ -156,8 +157,40 @@ Assert.AreEqual("sheet2", ws2.Name); Assert.Throws(() => ws1.Name = "SHEET2"); + } + } + [Test] + public void HideWorksheet() + { + using (var ms = new MemoryStream()) + { + using (var wb = new XLWorkbook()) + { + wb.Worksheets.Add("VisibleSheet"); + wb.Worksheets.Add("HiddenSheet").Hide(); + wb.SaveAs(ms); + } + // unhide the hidden sheet + using (var wb = new XLWorkbook(ms)) + { + Assert.AreEqual(XLWorksheetVisibility.Visible, wb.Worksheet("VisibleSheet").Visibility); + Assert.AreEqual(XLWorksheetVisibility.Hidden, wb.Worksheet("HiddenSheet").Visibility); + + var ws = wb.Worksheet("HiddenSheet"); + ws.Unhide().Name = "NoAlsoVisible"; + + Assert.AreEqual(XLWorksheetVisibility.Visible, ws.Visibility); + + wb.Save(); + } + + using (var wb = new XLWorkbook(ms)) + { + Assert.AreEqual(XLWorksheetVisibility.Visible, wb.Worksheet("VisibleSheet").Visibility); + Assert.AreEqual(XLWorksheetVisibility.Visible, wb.Worksheet("NoAlsoVisible").Visibility); + } } } } diff --git a/ClosedXML_Tests/Resource/Examples/Columns/DeletingColumns.xlsx b/ClosedXML_Tests/Resource/Examples/Columns/DeletingColumns.xlsx index bac6e30..e2ac29f 100644 --- a/ClosedXML_Tests/Resource/Examples/Columns/DeletingColumns.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Columns/DeletingColumns.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Comments/AddingComments.xlsx b/ClosedXML_Tests/Resource/Examples/Comments/AddingComments.xlsx index 3879998..5c9ae0a 100644 --- a/ClosedXML_Tests/Resource/Examples/Comments/AddingComments.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Comments/AddingComments.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFIsBlank.xlsx b/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFIsBlank.xlsx index 80be4ab..e57b38d 100644 --- a/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFIsBlank.xlsx +++ b/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFIsBlank.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFNotBlank.xlsx b/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFNotBlank.xlsx index 5f4f6ce..e233171 100644 --- a/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFNotBlank.xlsx +++ b/ClosedXML_Tests/Resource/Examples/ConditionalFormatting/CFNotBlank.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageAnchors.xlsx b/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageAnchors.xlsx index 53aa802..d2a90ba 100644 --- a/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageAnchors.xlsx +++ b/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageAnchors.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageFormats.xlsx b/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageFormats.xlsx index 237d910..3ec78e0 100644 --- a/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageFormats.xlsx +++ b/ClosedXML_Tests/Resource/Examples/ImageHandling/ImageFormats.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/BlankCells.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/BlankCells.xlsx index 21ea101..3f4195a 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/BlankCells.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/BlankCells.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/DataTypes.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/DataTypes.xlsx index 87f74be..d7b9afd 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/DataTypes.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/DataTypes.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/DataTypesUnderDifferentCulture.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/DataTypesUnderDifferentCulture.xlsx index 5d0b538..f451273 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/DataTypesUnderDifferentCulture.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/DataTypesUnderDifferentCulture.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/HideUnhide.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/HideUnhide.xlsx index a3c6ea5..1f99477 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/HideUnhide.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/HideUnhide.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/Hyperlinks.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/Hyperlinks.xlsx index 222ba7b..d724e36 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/Hyperlinks.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/Hyperlinks.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/InsertingData.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/InsertingData.xlsx index c8ae77a..d9d1a57 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/InsertingData.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/InsertingData.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/InsertingTables.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/InsertingTables.xlsx deleted file mode 100644 index 212cbfe..0000000 --- a/ClosedXML_Tests/Resource/Examples/Misc/InsertingTables.xlsx +++ /dev/null Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/MergeMoves.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/MergeMoves.xlsx index 9839a6f..2cbaac9 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/MergeMoves.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/MergeMoves.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Misc/ShiftingFormulas.xlsx b/ClosedXML_Tests/Resource/Examples/Misc/ShiftingFormulas.xlsx index 81bc2a4..a1aa8d0 100644 --- a/ClosedXML_Tests/Resource/Examples/Misc/ShiftingFormulas.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Misc/ShiftingFormulas.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/PivotTables/PivotTables.xlsx b/ClosedXML_Tests/Resource/Examples/PivotTables/PivotTables.xlsx index 7c0b68e..369bb0c 100644 --- a/ClosedXML_Tests/Resource/Examples/PivotTables/PivotTables.xlsx +++ b/ClosedXML_Tests/Resource/Examples/PivotTables/PivotTables.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/ClearingRanges.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/ClearingRanges.xlsx index c1c9431..5e18500 100644 --- a/ClosedXML_Tests/Resource/Examples/Ranges/ClearingRanges.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Ranges/ClearingRanges.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/InsertingDeletingRows.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/InsertingDeletingRows.xlsx index 6cdd101..9588f7d 100644 --- a/ClosedXML_Tests/Resource/Examples/Ranges/InsertingDeletingRows.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Ranges/InsertingDeletingRows.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/SelectingRanges.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/SelectingRanges.xlsx index a33e2a6..adecfee 100644 --- a/ClosedXML_Tests/Resource/Examples/Ranges/SelectingRanges.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Ranges/SelectingRanges.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/ShiftingRanges.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/ShiftingRanges.xlsx index 3f1bafd..453328f 100644 --- a/ClosedXML_Tests/Resource/Examples/Ranges/ShiftingRanges.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Ranges/ShiftingRanges.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/SortExample.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/SortExample.xlsx index 8b26d86..bd2c3d6 100644 --- a/ClosedXML_Tests/Resource/Examples/Ranges/SortExample.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Ranges/SortExample.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/TransposeRangesPlus.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/TransposeRangesPlus.xlsx index 44fbbbe..037fc86 100644 --- a/ClosedXML_Tests/Resource/Examples/Ranges/TransposeRangesPlus.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Ranges/TransposeRangesPlus.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/UsingTables.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/UsingTables.xlsx deleted file mode 100644 index 288d1fe..0000000 --- a/ClosedXML_Tests/Resource/Examples/Ranges/UsingTables.xlsx +++ /dev/null Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Ranges/WalkingRanges.xlsx b/ClosedXML_Tests/Resource/Examples/Ranges/WalkingRanges.xlsx index 4a705f7..e12dfc5 100644 --- a/ClosedXML_Tests/Resource/Examples/Ranges/WalkingRanges.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Ranges/WalkingRanges.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Styles/UsingPhonetics.xlsx b/ClosedXML_Tests/Resource/Examples/Styles/UsingPhonetics.xlsx new file mode 100644 index 0000000..86fe3e6 --- /dev/null +++ b/ClosedXML_Tests/Resource/Examples/Styles/UsingPhonetics.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Tables/InsertingTables.xlsx b/ClosedXML_Tests/Resource/Examples/Tables/InsertingTables.xlsx new file mode 100644 index 0000000..89de679 --- /dev/null +++ b/ClosedXML_Tests/Resource/Examples/Tables/InsertingTables.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Tables/ResizingTables.xlsx b/ClosedXML_Tests/Resource/Examples/Tables/ResizingTables.xlsx new file mode 100644 index 0000000..dad3954 --- /dev/null +++ b/ClosedXML_Tests/Resource/Examples/Tables/ResizingTables.xlsx Binary files differ diff --git a/ClosedXML_Tests/Resource/Examples/Tables/UsingTables.xlsx b/ClosedXML_Tests/Resource/Examples/Tables/UsingTables.xlsx new file mode 100644 index 0000000..288d1fe --- /dev/null +++ b/ClosedXML_Tests/Resource/Examples/Tables/UsingTables.xlsx Binary files differ