/******************************************************************************* * You may amend and distribute as you like, but don't remove this header! * * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. * See http://www.codeplex.com/EPPlus for details. * * Copyright (C) 2011 Jan Källman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html * * All code and executables are provided "as is" with no warranty either express or implied. * The author accepts no liability for any damage or loss of business that this product may cause. * * Code change notes: * * Author Change Date * ****************************************************************************** * Mats Alm Added 2011-01-01 * Mats Alm Applying patch submitted 2011-11-14 * by Ted Heatherington * Jan Källman License changed GPL-->LGPL 2011-12-27 * Raziq York Added support for Any type 2014-08-08 *******************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using OfficeOpenXml.Utils; using System.Xml; using OfficeOpenXml.DataValidation.Contracts; namespace OfficeOpenXml.DataValidation { /// /// /// Collection of . This class is providing the API for EPPlus data validation. /// /// /// The public methods of this class (Add[...]Validation) will create a datavalidation entry in the worksheet. When this /// validation has been created changes to the properties will affect the workbook immediately. /// /// /// Each type of validation has either a formula or a typed value/values, except for custom validation which has a formula only. /// /// /// // Add a date time validation /// var validation = worksheet.DataValidation.AddDateTimeValidation("A1"); /// // set validation properties /// validation.ShowErrorMessage = true; /// validation.ErrorTitle = "An invalid date was entered"; /// validation.Error = "The date must be between 2011-01-31 and 2011-12-31"; /// validation.Prompt = "Enter date here"; /// validation.Formula.Value = DateTime.Parse("2011-01-01"); /// validation.Formula2.Value = DateTime.Parse("2011-12-31"); /// validation.Operator = ExcelDataValidationOperator.between; /// /// public class ExcelDataValidationCollection : XmlHelper, IEnumerable { private List _validations = new List(); private ExcelWorksheet _worksheet = null; private const string DataValidationPath = "//d:dataValidations"; private readonly string DataValidationItemsPath = string.Format("{0}/d:dataValidation", DataValidationPath); /// /// Constructor /// /// internal ExcelDataValidationCollection(ExcelWorksheet worksheet) : base(worksheet.NameSpaceManager, worksheet.WorksheetXml.DocumentElement) { Require.Argument(worksheet).IsNotNull("worksheet"); _worksheet = worksheet; SchemaNodeOrder = worksheet.SchemaNodeOrder; // check existing nodes and load them var dataValidationNodes = worksheet.WorksheetXml.SelectNodes(DataValidationItemsPath, worksheet.NameSpaceManager); if (dataValidationNodes != null && dataValidationNodes.Count > 0) { foreach (XmlNode node in dataValidationNodes) { if (node.Attributes["sqref"] == null) continue; var addr = node.Attributes["sqref"].Value; var typeSchema = node.Attributes["type"] != null ? node.Attributes["type"].Value : ""; var type = ExcelDataValidationType.GetBySchemaName(typeSchema); _validations.Add(ExcelDataValidationFactory.Create(type, worksheet, addr, node)); } } if (_validations.Count > 0) { OnValidationCountChanged(); } } private void EnsureRootElementExists() { var node = _worksheet.WorksheetXml.SelectSingleNode(DataValidationPath, _worksheet.NameSpaceManager); if (node == null) { CreateNode(DataValidationPath.TrimStart('/')); } } private void OnValidationCountChanged() { //if (TopNode != null) //{ // SetXmlNodeString("@count", _validations.Count.ToString()); //} } private XmlNode GetRootNode() { EnsureRootElementExists(); TopNode = _worksheet.WorksheetXml.SelectSingleNode(DataValidationPath, _worksheet.NameSpaceManager); return TopNode; } /// /// Validates address - not empty, collisions /// /// /// private void ValidateAddress(string address, IExcelDataValidation validatingValidation) { Require.Argument(address).IsNotNullOrEmpty("address"); // ensure that the new address does not collide with an existing validation. var newAddress = new ExcelAddress(address); if (_validations.Count > 0) { foreach (var validation in _validations) { if (validatingValidation != null && validatingValidation == validation) { continue; } var result = validation.Address.Collide(newAddress); if (result != ExcelAddressBase.eAddressCollition.No) { throw new InvalidOperationException(string.Format("The address ({0}) collides with an existing validation ({1})", address, validation.Address.Address)); } } } } private void ValidateAddress(string address) { ValidateAddress(address, null); } /// /// Validates all data validations. /// internal void ValidateAll() { foreach (var validation in _validations) { validation.Validate(); ValidateAddress(validation.Address.Address, validation); } } /// /// Adds a to the worksheet. /// /// The range/address to validate /// public IExcelDataValidationAny AddAnyValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationAny(_worksheet, address, ExcelDataValidationType.Any); _validations.Add(item); OnValidationCountChanged(); return item; } /// /// Adds an to the worksheet. Whole means that the only accepted values /// are integer values. /// /// the range/address to validate public IExcelDataValidationInt AddIntegerValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationInt(_worksheet, address, ExcelDataValidationType.Whole); _validations.Add(item); OnValidationCountChanged(); return item; } /// /// Addes an to the worksheet. The only accepted values are /// decimal values. /// /// The range/address to validate /// public IExcelDataValidationDecimal AddDecimalValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationDecimal(_worksheet, address, ExcelDataValidationType.Decimal); _validations.Add(item); OnValidationCountChanged(); return item; } /// /// Adds an to the worksheet. The accepted values are defined /// in a list. /// /// The range/address to validate /// public IExcelDataValidationList AddListValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationList(_worksheet, address, ExcelDataValidationType.List); _validations.Add(item); OnValidationCountChanged(); return item; } /// /// Adds an regarding text length to the worksheet. /// /// The range/address to validate /// public IExcelDataValidationInt AddTextLengthValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationInt(_worksheet, address, ExcelDataValidationType.TextLength); _validations.Add(item); OnValidationCountChanged(); return item; } /// /// Adds an to the worksheet. /// /// The range/address to validate /// public IExcelDataValidationDateTime AddDateTimeValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationDateTime(_worksheet, address, ExcelDataValidationType.DateTime); _validations.Add(item); OnValidationCountChanged(); return item; } public IExcelDataValidationTime AddTimeValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationTime(_worksheet, address, ExcelDataValidationType.Time); _validations.Add(item); OnValidationCountChanged(); return item; } /// /// Adds a to the worksheet. /// /// The range/address to validate /// public IExcelDataValidationCustom AddCustomValidation(string address) { ValidateAddress(address); EnsureRootElementExists(); var item = new ExcelDataValidationCustom(_worksheet, address, ExcelDataValidationType.Custom); _validations.Add(item); OnValidationCountChanged(); return item; } /// /// Removes an from the collection. /// /// The item to remove /// True if remove succeeds, otherwise false /// if is null public bool Remove(IExcelDataValidation item) { if (!(item is ExcelDataValidation)) { throw new InvalidCastException("The supplied item must inherit OfficeOpenXml.DataValidation.ExcelDataValidation"); } Require.Argument(item).IsNotNull("item"); TopNode.RemoveChild(((ExcelDataValidation)item).TopNode); var retVal = _validations.Remove(item); if (retVal) OnValidationCountChanged(); return retVal; } /// /// Number of validations /// public int Count { get { return _validations.Count; } } /// /// Index operator, returns by 0-based index /// /// /// public IExcelDataValidation this[int index] { get { return _validations[index]; } set { _validations[index] = value; } } /// /// Index operator, returns a data validation which address partly or exactly matches the searched address. /// /// A cell address or range /// A or null if no match public IExcelDataValidation this[string address] { get { var searchedAddress = new ExcelAddress(address); return _validations.Find(x => x.Address.Collide(searchedAddress) != ExcelAddressBase.eAddressCollition.No); } } /// /// Returns all validations that matches the supplied predicate . /// /// predicate to filter out matching validations /// public IEnumerable FindAll(Predicate match) { return _validations.FindAll(match); } /// /// Returns the first matching validation. /// /// /// public IExcelDataValidation Find(Predicate match) { return _validations.Find(match); } /// /// Removes all validations from the collection. /// public void Clear() { DeleteAllNode(DataValidationItemsPath.TrimStart('/')); _validations.Clear(); } /// /// Removes the validations that matches the predicate /// /// public void RemoveAll(Predicate match) { var matches = _validations.FindAll(match); foreach (var m in matches) { if (!(m is ExcelDataValidation)) { throw new InvalidCastException("The supplied item must inherit OfficeOpenXml.DataValidation.ExcelDataValidation"); } TopNode.SelectSingleNode(DataValidationPath.TrimStart('/'), NameSpaceManager).RemoveChild(((ExcelDataValidation)m).TopNode); } _validations.RemoveAll(match); OnValidationCountChanged(); } IEnumerator IEnumerable.GetEnumerator() { return _validations.GetEnumerator(); } IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _validations.GetEnumerator(); } } }