| /******************************************************************************* | |
| * 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 | |
| * Jan Källman License changed GPL-->LGPL 2011-12-27 | |
| *******************************************************************************/ | |
| using System; | |
| using System.Collections.Immutable; | |
| using System.Globalization; | |
| using System.Text.RegularExpressions; | |
| using System.Xml; | |
| namespace AppsheetEpplus; | |
| /// <summary> | |
| /// Excel datavalidation | |
| /// </summary> | |
| public abstract class ExcelDataValidation : XmlHelper, IExcelDataValidation { | |
| private const string _itemElementNodeName = "d:dataValidation"; | |
| private readonly string _errorStylePath = "@errorStyle"; | |
| private readonly string _errorTitlePath = "@errorTitle"; | |
| private readonly string _errorPath = "@error"; | |
| private readonly string _promptTitlePath = "@promptTitle"; | |
| private readonly string _promptPath = "@prompt"; | |
| private readonly string _operatorPath = "@operator"; | |
| private readonly string _showErrorMessagePath = "@showErrorMessage"; | |
| private readonly string _showInputMessagePath = "@showInputMessage"; | |
| private readonly string _typeMessagePath = "@type"; | |
| private readonly string _sqrefPath = "@sqref"; | |
| private readonly string _allowBlankPath = "@allowBlank"; | |
| protected readonly string _formula1Path = "d:formula1"; | |
| protected readonly string _formula2Path = "d:formula2"; | |
| protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ | |
| "type", | |
| "errorStyle", | |
| "operator", | |
| "allowBlank", | |
| "showInputMessage", | |
| "showErrorMessage", | |
| "errorTitle", | |
| "error", | |
| "promptTitle", | |
| "prompt", | |
| "sqref", | |
| "formula1", | |
| "formula2", | |
| ]; | |
| internal ExcelDataValidation( | |
| ExcelWorksheet worksheet, | |
| string address, | |
| ExcelDataValidationType validationType) | |
| : this(worksheet, address, validationType, null) {} | |
| /// <summary> | |
| /// Constructor | |
| /// </summary> | |
| /// <param name="worksheet">worksheet that owns the validation</param> | |
| /// <param name="itemElementNode">Xml top node (dataValidations)</param> | |
| /// <param name="validationType">Data validation type</param> | |
| /// <param name="address">address for data validation</param> | |
| internal ExcelDataValidation( | |
| ExcelWorksheet worksheet, | |
| string address, | |
| ExcelDataValidationType validationType, | |
| XmlNode itemElementNode) | |
| : this(worksheet, address, validationType, itemElementNode, null) {} | |
| /// <summary> | |
| /// Constructor | |
| /// </summary> | |
| /// <param name="worksheet">worksheet that owns the validation</param> | |
| /// <param name="itemElementNode">Xml top node (dataValidations) when importing xml</param> | |
| /// <param name="validationType">Data validation type</param> | |
| /// <param name="address">address for data validation</param> | |
| /// <param name="namespaceManager">Xml Namespace manager</param> | |
| internal ExcelDataValidation( | |
| ExcelWorksheet worksheet, | |
| string address, | |
| ExcelDataValidationType validationType, | |
| XmlNode itemElementNode, | |
| XmlNamespaceManager namespaceManager) | |
| : base(namespaceManager ?? worksheet.NameSpaceManager) { | |
| ArgumentException.ThrowIfNullOrEmpty(address); | |
| address = CheckAndFixRangeAddress(address); | |
| if (itemElementNode == null) { | |
| //var xmlDoc = worksheet.WorksheetXml; | |
| TopNode = worksheet.WorksheetXml.SelectSingleNode( | |
| "//d:dataValidations", | |
| worksheet.NameSpaceManager); | |
| // did not succeed using the XmlHelper methods here... so I'm creating the new node using XmlDocument... | |
| var nsUri = NameSpaceManager.LookupNamespace("d"); | |
| //itemElementNode = TopNode.OwnerDocument.CreateElement(_itemElementNodeName, nsUri); | |
| itemElementNode = TopNode.OwnerDocument.CreateElement( | |
| _itemElementNodeName.Split(':')[1], | |
| nsUri); | |
| TopNode.AppendChild(itemElementNode); | |
| } | |
| TopNode = itemElementNode; | |
| ValidationType = validationType; | |
| Address = new(address); | |
| } | |
| private string CheckAndFixRangeAddress(string address) { | |
| if (address.Contains(',')) { | |
| throw new FormatException("Multiple addresses may not be commaseparated, use space instead"); | |
| } | |
| address = address.ToUpper(CultureInfo.InvariantCulture); | |
| if (Regex.IsMatch(address, "[A-Z]+:[A-Z]+")) { | |
| address = AddressUtility.ParseEntireColumnSelections(address); | |
| } | |
| return address; | |
| } | |
| private void SetNullableBoolValue(string path, bool? val) { | |
| if (val.HasValue) { | |
| SetXmlNodeBool(path, val.Value); | |
| } else { | |
| DeleteNode(path); | |
| } | |
| } | |
| /// <summary> | |
| /// This method will validate the state of the validation | |
| /// </summary> | |
| /// <exception cref="InvalidOperationException">If the state breaks the rules of the validation</exception> | |
| public virtual void Validate() { | |
| var address = Address.Address; | |
| // validate Formula1 | |
| if (string.IsNullOrEmpty(Formula1Internal)) { | |
| throw new InvalidOperationException( | |
| "Validation of " + address + " failed: Formula1 cannot be empty"); | |
| } | |
| } | |
| /// <summary> | |
| /// True if the validation type allows operator to be set. | |
| /// </summary> | |
| public bool AllowsOperator => ValidationType.AllowOperator; | |
| /// <summary> | |
| /// Address of data validation | |
| /// </summary> | |
| public ExcelAddress Address { | |
| get => new(GetXmlNodeString(_sqrefPath)); | |
| private set { | |
| var address = AddressUtility.ParseEntireColumnSelections(value.Address); | |
| SetXmlNodeString(_sqrefPath, address); | |
| } | |
| } | |
| /// <summary> | |
| /// Validation type | |
| /// </summary> | |
| public ExcelDataValidationType ValidationType { | |
| get { | |
| var typeString = GetXmlNodeString(_typeMessagePath); | |
| return ExcelDataValidationType.GetBySchemaName(typeString); | |
| } | |
| private set => SetXmlNodeString(_typeMessagePath, value.SchemaName, true); | |
| } | |
| /// <summary> | |
| /// Operator for comparison between the entered value and Formula/Formulas. | |
| /// </summary> | |
| public ExcelDataValidationOperator Operator { | |
| get { | |
| var operatorString = GetXmlNodeString(_operatorPath); | |
| return Enum.TryParse<ExcelDataValidationOperator>(operatorString, true, out var op) | |
| ? op | |
| : ExcelDataValidationOperator.Any; | |
| } | |
| } | |
| /// <summary> | |
| /// Warning style | |
| /// </summary> | |
| public ExcelDataValidationWarningStyle ErrorStyle { | |
| get { | |
| var errorStyleString = GetXmlNodeString(_errorStylePath); | |
| return Enum.TryParse<ExcelDataValidationWarningStyle>(errorStyleString, true, out var style) | |
| ? style | |
| : ExcelDataValidationWarningStyle.Undefined; | |
| } | |
| } | |
| /// <summary> | |
| /// True if blanks should be allowed | |
| /// </summary> | |
| public bool? AllowBlank { | |
| get => GetXmlNodeBoolNullable(_allowBlankPath); | |
| set => SetNullableBoolValue(_allowBlankPath, value); | |
| } | |
| /// <summary> | |
| /// True if input message should be shown | |
| /// </summary> | |
| public bool? ShowInputMessage { | |
| get => GetXmlNodeBoolNullable(_showInputMessagePath); | |
| set => SetNullableBoolValue(_showInputMessagePath, value); | |
| } | |
| /// <summary> | |
| /// True if error message should be shown | |
| /// </summary> | |
| public bool? ShowErrorMessage { | |
| get => GetXmlNodeBoolNullable(_showErrorMessagePath); | |
| set => SetNullableBoolValue(_showErrorMessagePath, value); | |
| } | |
| /// <summary> | |
| /// Title of error message box | |
| /// </summary> | |
| public string ErrorTitle { | |
| get => GetXmlNodeString(_errorTitlePath); | |
| set => SetXmlNodeString(_errorTitlePath, value); | |
| } | |
| /// <summary> | |
| /// Error message box text | |
| /// </summary> | |
| public string Error { | |
| get => GetXmlNodeString(_errorPath); | |
| set => SetXmlNodeString(_errorPath, value); | |
| } | |
| public string PromptTitle { | |
| get => GetXmlNodeString(_promptTitlePath); | |
| set => SetXmlNodeString(_promptTitlePath, value); | |
| } | |
| public string Prompt { | |
| get => GetXmlNodeString(_promptPath); | |
| set => SetXmlNodeString(_promptPath, value); | |
| } | |
| /// <summary> | |
| /// Formula 1 | |
| /// </summary> | |
| protected string Formula1Internal => GetXmlNodeString(_formula1Path); | |
| /// <summary> | |
| /// Formula 2 | |
| /// </summary> | |
| protected string Formula2Internal => GetXmlNodeString(_formula2Path); | |
| protected void SetValue<T>(T? val, string path) | |
| where T : struct { | |
| if (!val.HasValue) { | |
| DeleteNode(path); | |
| } | |
| var stringValue = val.Value.ToString().Replace(',', '.'); | |
| SetXmlNodeString(path, stringValue); | |
| } | |
| } |