/******************************************************************************* * 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 * ****************************************************************************** * Eyal Seagull Conditional Formatting 2012-04-03 *******************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using OfficeOpenXml.Utils; using System.Xml; using OfficeOpenXml.ConditionalFormatting.Contracts; using System.Text.RegularExpressions; using System.Drawing; namespace OfficeOpenXml.ConditionalFormatting { /// /// Collection of . /// This class is providing the API for EPPlus conditional formatting. /// /// /// /// The public methods of this class (Add[...]ConditionalFormatting) will create a ConditionalFormatting/CfRule entry in the worksheet. When this /// Conditional Formatting has been created changes to the properties will affect the workbook immediately. /// /// /// Each type of Conditional Formatting Rule has diferente set of properties. /// /// /// // Add a Three Color Scale conditional formatting /// var cf = worksheet.ConditionalFormatting.AddThreeColorScale(new ExcelAddress("A1:C10")); /// // Set the conditional formatting properties /// cf.LowValue.Type = ExcelConditionalFormattingValueObjectType.Min; /// cf.LowValue.Color = Color.White; /// cf.MiddleValue.Type = ExcelConditionalFormattingValueObjectType.Percent; /// cf.MiddleValue.Value = 50; /// cf.MiddleValue.Color = Color.Blue; /// cf.HighValue.Type = ExcelConditionalFormattingValueObjectType.Max; /// cf.HighValue.Color = Color.Black; /// /// public class ExcelConditionalFormattingCollection : XmlHelper, IEnumerable { /****************************************************************************************/ #region Private Properties private List _rules = new List(); private ExcelWorksheet _worksheet = null; #endregion Private Properties /****************************************************************************************/ #region Constructors /// /// Initialize the /// /// internal ExcelConditionalFormattingCollection( ExcelWorksheet worksheet) : base( worksheet.NameSpaceManager, worksheet.WorksheetXml.DocumentElement) { Require.Argument(worksheet).IsNotNull("worksheet"); _worksheet = worksheet; SchemaNodeOrder = _worksheet.SchemaNodeOrder; // Look for all the var conditionalFormattingNodes = TopNode.SelectNodes( "//" + ExcelConditionalFormattingConstants.Paths.ConditionalFormatting, _worksheet.NameSpaceManager); // Check if we found at least 1 node if ((conditionalFormattingNodes != null) && (conditionalFormattingNodes.Count > 0)) { // Foreach foreach (XmlNode conditionalFormattingNode in conditionalFormattingNodes) { // Check if @sqref attribute exists if (conditionalFormattingNode.Attributes[ExcelConditionalFormattingConstants.Attributes.Sqref] == null) { throw new Exception( ExcelConditionalFormattingConstants.Errors.MissingSqrefAttribute); } // Get the @sqref attribute ExcelAddress address = new ExcelAddress( conditionalFormattingNode.Attributes[ExcelConditionalFormattingConstants.Attributes.Sqref].Value); // Check for all the nodes and load them var cfRuleNodes = conditionalFormattingNode.SelectNodes( ExcelConditionalFormattingConstants.Paths.CfRule, _worksheet.NameSpaceManager); // Foreach inside the current foreach (XmlNode cfRuleNode in cfRuleNodes) { // Check if @type attribute exists if (cfRuleNode.Attributes[ExcelConditionalFormattingConstants.Attributes.Type] == null) { throw new Exception( ExcelConditionalFormattingConstants.Errors.MissingTypeAttribute); } // Check if @priority attribute exists if (cfRuleNode.Attributes[ExcelConditionalFormattingConstants.Attributes.Priority] == null) { throw new Exception( ExcelConditionalFormattingConstants.Errors.MissingPriorityAttribute); } // Get the main attributes string typeAttribute = ExcelConditionalFormattingHelper.GetAttributeString( cfRuleNode, ExcelConditionalFormattingConstants.Attributes.Type); int priority = ExcelConditionalFormattingHelper.GetAttributeInt( cfRuleNode, ExcelConditionalFormattingConstants.Attributes.Priority); // Transform the @type attribute to EPPlus Rule Type (slighty diferente) var type = ExcelConditionalFormattingRuleType.GetTypeByAttrbiute( typeAttribute, cfRuleNode, _worksheet.NameSpaceManager); // Create the Rule according to the correct type, address and priority var cfRule = ExcelConditionalFormattingRuleFactory.Create( type, address, priority, _worksheet, cfRuleNode); // Add the new rule to the list if(cfRule!=null) _rules.Add(cfRule); } } } } #endregion Constructors /****************************************************************************************/ #region Methods /// /// /// private void EnsureRootElementExists() { // Find the node if (_worksheet.WorksheetXml.DocumentElement == null) { throw new Exception( ExcelConditionalFormattingConstants.Errors.MissingWorksheetNode); } } /// /// GetRootNode /// /// private XmlNode GetRootNode() { EnsureRootElementExists(); return _worksheet.WorksheetXml.DocumentElement; } /// /// Validates address - not empty (collisions are allowded) /// /// /// private ExcelAddress ValidateAddress( ExcelAddress address) { Require.Argument(address).IsNotNull("address"); //TODO: Are there any other validation we need to do? return address; } /// /// Get the next priority sequencial number /// /// private int GetNextPriority() { // Consider zero as the last priority when we have no CF rules int lastPriority = 0; // Search for the last priority foreach (var cfRule in _rules) { if (cfRule.Priority > lastPriority) { lastPriority = cfRule.Priority; } } // Our next priority is the last plus one return lastPriority + 1; } #endregion Methods /****************************************************************************************/ #region IEnumerable /// /// Number of validations /// public int Count { get { return _rules.Count; } } /// /// Index operator, returns by 0-based index /// /// /// public IExcelConditionalFormattingRule this[int index] { get { return _rules[index]; } set { _rules[index] = value; } } /// /// Get the 'cfRule' enumerator /// /// IEnumerator IEnumerable.GetEnumerator() { return _rules.GetEnumerator(); } /// /// Get the 'cfRule' enumerator /// /// IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _rules.GetEnumerator(); } /// /// Removes all 'cfRule' from the collection and from the XML. /// /// This is the same as removing all the 'conditionalFormatting' nodes. /// /// public void RemoveAll() { // Look for all the nodes var conditionalFormattingNodes = TopNode.SelectNodes( "//" + ExcelConditionalFormattingConstants.Paths.ConditionalFormatting, _worksheet.NameSpaceManager); // Remove all the nodes one by one foreach (XmlNode conditionalFormattingNode in conditionalFormattingNodes) { conditionalFormattingNode.ParentNode.RemoveChild(conditionalFormattingNode); } // Clear the item list _rules.Clear(); } /// /// Remove a Conditional Formatting Rule by its object /// /// public void Remove( IExcelConditionalFormattingRule item) { Require.Argument(item).IsNotNull("item"); try { // Point to the parent node var oldParentNode = item.Node.ParentNode; // Remove the from the old parent node oldParentNode.RemoveChild(item.Node); // Check if the old parent node has node inside it if (!oldParentNode.HasChildNodes) { // Remove the old parent node oldParentNode.ParentNode.RemoveChild(oldParentNode); } _rules.Remove(item); } catch { throw new Exception( ExcelConditionalFormattingConstants.Errors.InvalidRemoveRuleOperation); } } /// /// Remove a Conditional Formatting Rule by its 0-based index /// /// public void RemoveAt( int index) { Require.Argument(index).IsInRange(0, this.Count - 1, "index"); Remove(this[index]); } /// /// Remove a Conditional Formatting Rule by its priority /// /// public void RemoveByPriority( int priority) { try { Remove(RulesByPriority(priority)); } catch { } } /// /// Get a rule by its priority /// /// /// public IExcelConditionalFormattingRule RulesByPriority( int priority) { return _rules.Find(x => x.Priority == priority); } #endregion IEnumerable /****************************************************************************************/ #region Conditional Formatting Rules /// /// Add rule (internal) /// /// /// /// F internal IExcelConditionalFormattingRule AddRule( eExcelConditionalFormattingRuleType type, ExcelAddress address) { Require.Argument(address).IsNotNull("address"); address = ValidateAddress(address); EnsureRootElementExists(); // Create the Rule according to the correct type, address and priority IExcelConditionalFormattingRule cfRule = ExcelConditionalFormattingRuleFactory.Create( type, address, GetNextPriority(), _worksheet, null); // Add the newly created rule to the list _rules.Add(cfRule); // Return the newly created rule return cfRule; } /// /// Add AboveAverage Rule /// /// /// public IExcelConditionalFormattingAverageGroup AddAboveAverage( ExcelAddress address) { return (IExcelConditionalFormattingAverageGroup)AddRule( eExcelConditionalFormattingRuleType.AboveAverage, address); } /// /// Add AboveOrEqualAverage Rule /// /// /// public IExcelConditionalFormattingAverageGroup AddAboveOrEqualAverage( ExcelAddress address) { return (IExcelConditionalFormattingAverageGroup)AddRule( eExcelConditionalFormattingRuleType.AboveOrEqualAverage, address); } /// /// Add BelowAverage Rule /// /// /// public IExcelConditionalFormattingAverageGroup AddBelowAverage( ExcelAddress address) { return (IExcelConditionalFormattingAverageGroup)AddRule( eExcelConditionalFormattingRuleType.BelowAverage, address); } /// /// Add BelowOrEqualAverage Rule /// /// /// public IExcelConditionalFormattingAverageGroup AddBelowOrEqualAverage( ExcelAddress address) { return (IExcelConditionalFormattingAverageGroup)AddRule( eExcelConditionalFormattingRuleType.BelowOrEqualAverage, address); } /// /// Add AboveStdDev Rule /// /// /// public IExcelConditionalFormattingStdDevGroup AddAboveStdDev( ExcelAddress address) { return (IExcelConditionalFormattingStdDevGroup)AddRule( eExcelConditionalFormattingRuleType.AboveStdDev, address); } /// /// Add BelowStdDev Rule /// /// /// public IExcelConditionalFormattingStdDevGroup AddBelowStdDev( ExcelAddress address) { return (IExcelConditionalFormattingStdDevGroup)AddRule( eExcelConditionalFormattingRuleType.BelowStdDev, address); } /// /// Add Bottom Rule /// /// /// public IExcelConditionalFormattingTopBottomGroup AddBottom( ExcelAddress address) { return (IExcelConditionalFormattingTopBottomGroup)AddRule( eExcelConditionalFormattingRuleType.Bottom, address); } /// /// Add BottomPercent Rule /// /// /// public IExcelConditionalFormattingTopBottomGroup AddBottomPercent( ExcelAddress address) { return (IExcelConditionalFormattingTopBottomGroup)AddRule( eExcelConditionalFormattingRuleType.BottomPercent, address); } /// /// Add Top Rule /// /// /// public IExcelConditionalFormattingTopBottomGroup AddTop( ExcelAddress address) { return (IExcelConditionalFormattingTopBottomGroup)AddRule( eExcelConditionalFormattingRuleType.Top, address); } /// /// Add TopPercent Rule /// /// /// public IExcelConditionalFormattingTopBottomGroup AddTopPercent( ExcelAddress address) { return (IExcelConditionalFormattingTopBottomGroup)AddRule( eExcelConditionalFormattingRuleType.TopPercent, address); } /// /// Add Last7Days Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddLast7Days( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.Last7Days, address); } /// /// Add LastMonth Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddLastMonth( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.LastMonth, address); } /// /// Add LastWeek Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddLastWeek( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.LastWeek, address); } /// /// Add NextMonth Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddNextMonth( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.NextMonth, address); } /// /// Add NextWeek Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddNextWeek( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.NextWeek, address); } /// /// Add ThisMonth Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddThisMonth( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.ThisMonth, address); } /// /// Add ThisWeek Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddThisWeek( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.ThisWeek, address); } /// /// Add Today Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddToday( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.Today, address); } /// /// Add Tomorrow Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddTomorrow( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.Tomorrow, address); } /// /// Add Yesterday Rule /// /// /// public IExcelConditionalFormattingTimePeriodGroup AddYesterday( ExcelAddress address) { return (IExcelConditionalFormattingTimePeriodGroup)AddRule( eExcelConditionalFormattingRuleType.Yesterday, address); } /// /// Add BeginsWith Rule /// /// /// public IExcelConditionalFormattingBeginsWith AddBeginsWith( ExcelAddress address) { return (IExcelConditionalFormattingBeginsWith)AddRule( eExcelConditionalFormattingRuleType.BeginsWith, address); } /// /// Add Between Rule /// /// /// public IExcelConditionalFormattingBetween AddBetween( ExcelAddress address) { return (IExcelConditionalFormattingBetween)AddRule( eExcelConditionalFormattingRuleType.Between, address); } /// /// Add ContainsBlanks Rule /// /// /// public IExcelConditionalFormattingContainsBlanks AddContainsBlanks( ExcelAddress address) { return (IExcelConditionalFormattingContainsBlanks)AddRule( eExcelConditionalFormattingRuleType.ContainsBlanks, address); } /// /// Add ContainsErrors Rule /// /// /// public IExcelConditionalFormattingContainsErrors AddContainsErrors( ExcelAddress address) { return (IExcelConditionalFormattingContainsErrors)AddRule( eExcelConditionalFormattingRuleType.ContainsErrors, address); } /// /// Add ContainsText Rule /// /// /// public IExcelConditionalFormattingContainsText AddContainsText( ExcelAddress address) { return (IExcelConditionalFormattingContainsText)AddRule( eExcelConditionalFormattingRuleType.ContainsText, address); } /// /// Add DuplicateValues Rule /// /// /// public IExcelConditionalFormattingDuplicateValues AddDuplicateValues( ExcelAddress address) { return (IExcelConditionalFormattingDuplicateValues)AddRule( eExcelConditionalFormattingRuleType.DuplicateValues, address); } /// /// Add EndsWith Rule /// /// /// public IExcelConditionalFormattingEndsWith AddEndsWith( ExcelAddress address) { return (IExcelConditionalFormattingEndsWith)AddRule( eExcelConditionalFormattingRuleType.EndsWith, address); } /// /// Add Equal Rule /// /// /// public IExcelConditionalFormattingEqual AddEqual( ExcelAddress address) { return (IExcelConditionalFormattingEqual)AddRule( eExcelConditionalFormattingRuleType.Equal, address); } /// /// Add Expression Rule /// /// /// public IExcelConditionalFormattingExpression AddExpression( ExcelAddress address) { return (IExcelConditionalFormattingExpression)AddRule( eExcelConditionalFormattingRuleType.Expression, address); } /// /// Add GreaterThan Rule /// /// /// public IExcelConditionalFormattingGreaterThan AddGreaterThan( ExcelAddress address) { return (IExcelConditionalFormattingGreaterThan)AddRule( eExcelConditionalFormattingRuleType.GreaterThan, address); } /// /// Add GreaterThanOrEqual Rule /// /// /// public IExcelConditionalFormattingGreaterThanOrEqual AddGreaterThanOrEqual( ExcelAddress address) { return (IExcelConditionalFormattingGreaterThanOrEqual)AddRule( eExcelConditionalFormattingRuleType.GreaterThanOrEqual, address); } /// /// Add LessThan Rule /// /// /// public IExcelConditionalFormattingLessThan AddLessThan( ExcelAddress address) { return (IExcelConditionalFormattingLessThan)AddRule( eExcelConditionalFormattingRuleType.LessThan, address); } /// /// Add LessThanOrEqual Rule /// /// /// public IExcelConditionalFormattingLessThanOrEqual AddLessThanOrEqual( ExcelAddress address) { return (IExcelConditionalFormattingLessThanOrEqual)AddRule( eExcelConditionalFormattingRuleType.LessThanOrEqual, address); } /// /// Add NotBetween Rule /// /// /// public IExcelConditionalFormattingNotBetween AddNotBetween( ExcelAddress address) { return (IExcelConditionalFormattingNotBetween)AddRule( eExcelConditionalFormattingRuleType.NotBetween, address); } /// /// Add NotContainsBlanks Rule /// /// /// public IExcelConditionalFormattingNotContainsBlanks AddNotContainsBlanks( ExcelAddress address) { return (IExcelConditionalFormattingNotContainsBlanks)AddRule( eExcelConditionalFormattingRuleType.NotContainsBlanks, address); } /// /// Add NotContainsErrors Rule /// /// /// public IExcelConditionalFormattingNotContainsErrors AddNotContainsErrors( ExcelAddress address) { return (IExcelConditionalFormattingNotContainsErrors)AddRule( eExcelConditionalFormattingRuleType.NotContainsErrors, address); } /// /// Add NotContainsText Rule /// /// /// public IExcelConditionalFormattingNotContainsText AddNotContainsText( ExcelAddress address) { return (IExcelConditionalFormattingNotContainsText)AddRule( eExcelConditionalFormattingRuleType.NotContainsText, address); } /// /// Add NotEqual Rule /// /// /// public IExcelConditionalFormattingNotEqual AddNotEqual( ExcelAddress address) { return (IExcelConditionalFormattingNotEqual)AddRule( eExcelConditionalFormattingRuleType.NotEqual, address); } /// /// Add Unique Rule /// /// /// public IExcelConditionalFormattingUniqueValues AddUniqueValues( ExcelAddress address) { return (IExcelConditionalFormattingUniqueValues)AddRule( eExcelConditionalFormattingRuleType.UniqueValues, address); } /// /// Add ThreeColorScale Rule /// /// /// public IExcelConditionalFormattingThreeColorScale AddThreeColorScale( ExcelAddress address) { return (IExcelConditionalFormattingThreeColorScale)AddRule( eExcelConditionalFormattingRuleType.ThreeColorScale, address); } /// /// Add TwoColorScale Rule /// /// /// public IExcelConditionalFormattingTwoColorScale AddTwoColorScale( ExcelAddress address) { return (IExcelConditionalFormattingTwoColorScale)AddRule( eExcelConditionalFormattingRuleType.TwoColorScale, address); } /// /// Add ThreeIconSet Rule /// /// The address /// Type of iconset /// public IExcelConditionalFormattingThreeIconSet AddThreeIconSet(ExcelAddress Address, eExcelconditionalFormatting3IconsSetType IconSet) { var icon = (IExcelConditionalFormattingThreeIconSet)AddRule( eExcelConditionalFormattingRuleType.ThreeIconSet, Address); icon.IconSet = IconSet; return icon; } /// /// Adds a FourIconSet rule /// /// /// /// public IExcelConditionalFormattingFourIconSet AddFourIconSet(ExcelAddress Address, eExcelconditionalFormatting4IconsSetType IconSet) { var icon = (IExcelConditionalFormattingFourIconSet)AddRule( eExcelConditionalFormattingRuleType.FourIconSet, Address); icon.IconSet = IconSet; return icon; } /// /// Adds a FiveIconSet rule /// /// /// /// public IExcelConditionalFormattingFiveIconSet AddFiveIconSet(ExcelAddress Address, eExcelconditionalFormatting5IconsSetType IconSet) { var icon = (IExcelConditionalFormattingFiveIconSet)AddRule( eExcelConditionalFormattingRuleType.FiveIconSet, Address); icon.IconSet = IconSet; return icon; } /// /// Adds a databar rule /// /// /// /// public IExcelConditionalFormattingDataBarGroup AddDatabar(ExcelAddress Address, Color color) { var dataBar = (IExcelConditionalFormattingDataBarGroup)AddRule( eExcelConditionalFormattingRuleType.DataBar, Address); dataBar.Color=color; return dataBar; } #endregion Conditional Formatting Rules } }