From e1ca6fe931525e810fe49e74b6a2867e815af814 Mon Sep 17 00:00:00 2001 From: Kris Wragg Date: Sat, 24 Aug 2019 18:04:12 +0100 Subject: [PATCH 1/2] Created ExcelChartErrorBar class and applied to ExcelBarChartSerie class. Added Sample_ErrorBars as an example of how to use with data to allow for an XY scatter example once that is done. --- EPPlus/Drawing/Chart/ExcelBarChartSerie.cs | 16 ++ EPPlus/Drawing/Chart/ExcelChart.cs | 53 +++++ EPPlus/Drawing/Chart/ExcelChartErrorBar.cs | 245 +++++++++++++++++++++ EPPlus/Drawing/Chart/ExcelChartSerie.cs | 2 +- EPPlus/EPPlus.csproj | 1 + SampleApp/EPPlusSamples.csproj | 1 + SampleApp/Sample_ErrorBars.cs | 60 +++++ SampleApp/Sample_Main.cs | 7 +- 8 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 EPPlus/Drawing/Chart/ExcelChartErrorBar.cs create mode 100644 SampleApp/Sample_ErrorBars.cs diff --git a/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs b/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs index fa90cbb6e..9c02788e6 100644 --- a/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs +++ b/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs @@ -79,5 +79,21 @@ internal bool InvertIfNegative SetXmlNodeBool(INVERTIFNEGATIVE_PATH, value); } } + + const string _errBarPath = "c:errBars"; + + ExcelChartErrorBar _errorBar = null; + public ExcelChartErrorBar ErrorBar + { + get + { + if (_errorBar == null) + { + var node = CreateNode(_errBarPath); + _errorBar = new ExcelChartErrorBar(this, NameSpaceManager, node); + } + return _errorBar; + } + } } } diff --git a/EPPlus/Drawing/Chart/ExcelChart.cs b/EPPlus/Drawing/Chart/ExcelChart.cs index 6cde30585..7cf9251a1 100644 --- a/EPPlus/Drawing/Chart/ExcelChart.cs +++ b/EPPlus/Drawing/Chart/ExcelChart.cs @@ -346,6 +346,59 @@ public enum eSizeRepresents /// Width } + + public enum eErrorBarType + { + /// + /// Specifies that error bars shall be shown in the positive and negative directions. + /// + Both, + /// + /// Specifies that error bars shall be shown in the negative direction only. + /// + Minus, + /// + /// Specifies that error bars shall be shown in the positive direction only. + /// + Plus + } + + public enum eErrorBarDirection + { + /// + /// Specifies that error bars shall be shown in the x direction. + /// + X, + /// + /// Specifies that error bars shall be shown in the y direction. + /// + Y + } + + public enum eErrorBarValueType + { + /// + /// Specifies that the length of the error bars shall be determined by the Plus and Minus elements. + /// + CustomErrorBars, + /// + /// Specifies that the length of the error bars shall be the fixed value determined by Error Bar Value. + /// + FixedValue, + /// + /// Specifies that the length of the error bars shall be Error Bar Value percent of the data. + /// + Percentage, + /// + /// Specifies that the length of the error bars shall be Error Bar Value standard deviations of the data. + /// + StandardDeviation, + /// + /// Specifies that the length of the error bars shall be Error Bar Value standard errors of the data. + /// + StandardError + } + #endregion /// /// Base class for Chart object. diff --git a/EPPlus/Drawing/Chart/ExcelChartErrorBar.cs b/EPPlus/Drawing/Chart/ExcelChartErrorBar.cs new file mode 100644 index 000000000..9fe317ca1 --- /dev/null +++ b/EPPlus/Drawing/Chart/ExcelChartErrorBar.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Xml; + +namespace OfficeOpenXml.Drawing.Chart +{ + public class ExcelChartErrorBar : XmlHelper + { + internal ExcelChartSerie _chartSerie; + protected XmlNode _node; + protected XmlNamespaceManager _ns; + + const string BARTYPEPATH = "c:errBarType/@val"; + const string VALTYPEPATH = "c:errValType/@val"; + const string NOENDCAPVALUEPATH = "c:noEndCap/@val"; + const string _errorBarValuePath = "c:val/@val"; + const string _minusErrorPath = "c:minus/c:numRef/c:f"; + const string _plusErrorPath = "c:plus/c:numRef/c:f"; + const string _minusErrorCachePath = "c:minus/c:numRef/c:numCache"; + const string _plusErrorCachePath = "c:plus/c:numRef/c:numCache"; + const string _minusErrorLitPath = "c:minus/c:numRef/c:numLit"; + const string _plusErrorLitPath = "c:plus/c:numRef/c:numLit"; + + internal ExcelChartErrorBar(ExcelChartSerie chartSerie, XmlNamespaceManager ns, XmlNode node) + : base(ns, node) + { + _chartSerie = chartSerie; + _node = node; + _ns = ns; + + SchemaNodeOrder = new string[] { "errDir", "errBarType", "errValType", "noEndCap", "plus", "minus", "val", "spPr" }; + } + + /// + /// The type of the error bars - positive, negative, or both. + /// + public eErrorBarType Type + { + get + { + switch (GetXmlNodeString(BARTYPEPATH).ToLower(CultureInfo.InvariantCulture)) + { + case "both": + return eErrorBarType.Both; + case "minus": + return eErrorBarType.Minus; + case "plus": + return eErrorBarType.Plus; + default: + return eErrorBarType.Both; + } + } + + set + { + switch (value) + { + case eErrorBarType.Both: + SetXmlNodeString(BARTYPEPATH, "both"); + break; + case eErrorBarType.Minus: + SetXmlNodeString(BARTYPEPATH, "minus"); + break; + case eErrorBarType.Plus: + SetXmlNodeString(BARTYPEPATH, "plus"); + break; + default: + SetXmlNodeString(BARTYPEPATH, "both"); + break; + } + } + } + + /// + /// The type of values used to determine the length of the error bars. + /// + public eErrorBarValueType ValueType + { + get + { + switch (GetXmlNodeString(VALTYPEPATH).ToLower(CultureInfo.InvariantCulture)) + { + case "cust": + return eErrorBarValueType.CustomErrorBars; + case "fixedVal": + return eErrorBarValueType.FixedValue; + case "percentage": + return eErrorBarValueType.Percentage; + case "stdDev": + return eErrorBarValueType.StandardDeviation; + case "stdErr": + return eErrorBarValueType.StandardError; + default: + return eErrorBarValueType.FixedValue; + } + } + + set + { + switch (value) + { + case eErrorBarValueType.CustomErrorBars: + SetXmlNodeString(VALTYPEPATH, "cust"); + break; + case eErrorBarValueType.FixedValue: + SetXmlNodeString(VALTYPEPATH, "fixedVal"); + break; + case eErrorBarValueType.Percentage: + SetXmlNodeString(VALTYPEPATH, "percentage"); + break; + case eErrorBarValueType.StandardDeviation: + SetXmlNodeString(VALTYPEPATH, "stdDev"); + break; + case eErrorBarValueType.StandardError: + SetXmlNodeString(VALTYPEPATH, "stdErr"); + break; + default: + SetXmlNodeString(VALTYPEPATH, "fixedVal"); + break; + } + } + } + + /// + /// This element specifies an end cap is not drawn on the error bars. + /// + public bool NoEndCap + { + get + { + return GetXmlNodeBool(NOENDCAPVALUEPATH, true); + } + + set + { + SetXmlNodeBool(NOENDCAPVALUEPATH, value, true); + } + } + + /// + /// Address range used for Custom value type + /// + public string MinusAddress + { + get + { + return GetXmlNodeString(_minusErrorPath); + } + + set + { + CreateNode(_minusErrorPath, true); + SetXmlNodeString(_minusErrorPath, ExcelCellBase.GetFullAddress(_chartSerie._chartSeries.Chart.WorkSheet.Name, value)); + + CleanupCacheAndLit(_minusErrorCachePath, _minusErrorLitPath); + } + } + + /// + /// Address range used for Custom value type + /// + public string PlusAddress + { + get + { + return GetXmlNodeString(_plusErrorPath); + } + + set + { + CreateNode(_plusErrorPath, true); + SetXmlNodeString(_plusErrorPath, ExcelCellBase.GetFullAddress(_chartSerie._chartSeries.Chart.WorkSheet.Name, value)); + + CleanupCacheAndLit(_plusErrorCachePath, _plusErrorLitPath); + } + } + + /// + /// This element specifies a value which is used with the Error Bar Type to determine the length of the error bars. + /// + public double Value + { + get + { + double? value = GetXmlNodeDoubleNull(_errorBarValuePath); + + switch(ValueType) + { + case eErrorBarValueType.CustomErrorBars: + throw new Exception("Error bar value is not valid for Custom Error Bars, use PlusAddress and MinusAddress"); + case eErrorBarValueType.StandardError: + throw new Exception("Error bar value is not valid for Standard Error"); + case eErrorBarValueType.FixedValue: + value = 0.1; + break; + case eErrorBarValueType.Percentage: + value = 5; + break; + case eErrorBarValueType.StandardDeviation: + value = 1.0; + break; + } + + return value.Value; + } + + set + { + SetXmlNodeString(_errorBarValuePath, value.ToString(CultureInfo.InvariantCulture)); + } + } + + ExcelDrawingBorder _fill = null; + public ExcelDrawingBorder Line + { + get + { + if (_fill == null) + { + _fill = new ExcelDrawingBorder(NameSpaceManager, TopNode, "c:spPr/a:ln"); + } + + return _fill; + } + } + + private void CleanupCacheAndLit(string cachePath, string litPath) + { + XmlNode cache = TopNode.SelectSingleNode(cachePath, _ns); + if (cache != null) + { + cache.ParentNode.RemoveChild(cache); + } + + XmlNode lit = TopNode.SelectSingleNode(litPath, _ns); + if (lit != null) + { + lit.ParentNode.RemoveChild(lit); + } + } + } +} diff --git a/EPPlus/Drawing/Chart/ExcelChartSerie.cs b/EPPlus/Drawing/Chart/ExcelChartSerie.cs index 4ded8e39f..c35d1c5b8 100644 --- a/EPPlus/Drawing/Chart/ExcelChartSerie.cs +++ b/EPPlus/Drawing/Chart/ExcelChartSerie.cs @@ -58,7 +58,7 @@ internal ExcelChartSerie(ExcelChartSeries chartSeries, XmlNamespaceManager ns, X _chartSeries = chartSeries; _node=node; _ns=ns; - SchemaNodeOrder = new string[] { "idx", "order","spPr", "tx", "marker", "trendline", "explosion","invertIfNegative", "dLbls", "cat", "val", "xVal", "yVal", "bubbleSize", "bubble3D", "smooth" }; + SchemaNodeOrder = new string[] { "idx", "order","spPr", "tx", "marker", "trendline", "explosion","invertIfNegative", "dLbls", "errBars", "cat", "val", "xVal", "yVal", "bubbleSize", "bubble3D", "smooth" }; if (chartSeries.Chart.ChartType == eChartType.XYScatter || chartSeries.Chart.ChartType == eChartType.XYScatterLines || diff --git a/EPPlus/EPPlus.csproj b/EPPlus/EPPlus.csproj index 1f411ce0d..ba927c0ee 100644 --- a/EPPlus/EPPlus.csproj +++ b/EPPlus/EPPlus.csproj @@ -225,6 +225,7 @@ + diff --git a/SampleApp/EPPlusSamples.csproj b/SampleApp/EPPlusSamples.csproj index 7b00fc136..5a1c53be2 100644 --- a/SampleApp/EPPlusSamples.csproj +++ b/SampleApp/EPPlusSamples.csproj @@ -75,6 +75,7 @@ + diff --git a/SampleApp/Sample_ErrorBars.cs b/SampleApp/Sample_ErrorBars.cs new file mode 100644 index 000000000..1e58873b6 --- /dev/null +++ b/SampleApp/Sample_ErrorBars.cs @@ -0,0 +1,60 @@ +using OfficeOpenXml; +using OfficeOpenXml.Drawing.Chart; + +namespace EPPlusSamples +{ + class Sample_ErrorBars + { + public static void RunSample_ErrorBars() + { + using (var package = new ExcelPackage()) + { + var ws = package.Workbook.Worksheets.Add("Error Bars"); + + var txt = "Sample,X,Y,X +error,X -error,Y +error,Y -error\r\n" + + "A,5,3,0.5,0.9,0.3,0.7\r\n" + + "B,6,4,0.6,0.8,0.4,0.6\r\n" + + "C,7,5,0.7,0.7,0.5,0.5\r\n" + + "D,8,6,0.8,0.6,0.6,0.4\r\n" + + "E,9,7,0.9,0.5,0.7,0.3\r\n"; + + ws.Cells["A1"].LoadFromText(txt); + + #region Add Column chart + { + var columnChart = (ExcelBarChart)ws.Drawings.AddChart("ColumnChart1", eChartType.ColumnClustered); + var columnSeries = (ExcelBarChartSerie)columnChart.Series.Add(ExcelCellBase.GetAddress(2, 2, 6, 2), ExcelCellBase.GetAddress(2, 1, 6, 1)); + columnChart.Style = eChartStyle.Style2; + columnChart.SetPosition(8, 0, 0, 0); + + columnSeries.ErrorBar.Type = eErrorBarType.Both; + columnSeries.ErrorBar.ValueType = eErrorBarValueType.CustomErrorBars; + columnSeries.ErrorBar.NoEndCap = false; + + columnSeries.ErrorBar.PlusAddress = "D2:D6"; + columnSeries.ErrorBar.MinusAddress = "E2:E6"; + + columnSeries.ErrorBar.Line.Fill.Color = System.Drawing.Color.Red; + } + #endregion + + #region Add Bar chart + { + var barChart = (ExcelBarChart)ws.Drawings.AddChart("BarChart1", eChartType.BarClustered); + var barSeries = (ExcelBarChartSerie)barChart.Series.Add(ExcelCellBase.GetAddress(2, 2, 6, 2), ExcelCellBase.GetAddress(2, 1, 6, 1)); + barChart.Style = eChartStyle.Style2; + barChart.SetPosition(19, 0, 0, 0); + + barSeries.ErrorBar.Type = eErrorBarType.Plus; + barSeries.ErrorBar.ValueType = eErrorBarValueType.Percentage; + barSeries.ErrorBar.Value = 10; + barSeries.ErrorBar.NoEndCap = false; + } + + #endregion + + package.SaveAs(Utils.GetFileInfo("Sample_ErrorBars.xlsx")); + } + } + } +} diff --git a/SampleApp/Sample_Main.cs b/SampleApp/Sample_Main.cs index f7d34dde6..ec044ca2f 100644 --- a/SampleApp/Sample_Main.cs +++ b/SampleApp/Sample_Main.cs @@ -170,8 +170,13 @@ static void Main(string[] args) Console.WriteLine("Running Sample_AddFormulaFunction"); Sample_AddFormulaFunction.RunSample_AddFormulaFunction(); Console.WriteLine(); + + // Sample ErrorBars - Shows how to add error bars to chart series + Console.WriteLine("Running RunSample_ErrorBars"); + Sample_ErrorBars.RunSample_ErrorBars(); + Console.WriteLine(); } - catch (Exception ex) + catch (Exception ex) { Console.WriteLine("Error: {0}", ex.Message); } From 5f128011054410677baf5f275ed9b0b179a5f58a Mon Sep 17 00:00:00 2001 From: Kris Wragg Date: Sun, 25 Aug 2019 14:31:36 +0100 Subject: [PATCH 2/2] Extending error bar functionality to scatter and line chart serie, fixing inability to delete error bars once they have been added. --- EPPlus/Drawing/Chart/ExcelBarChartSerie.cs | 43 ++++++++-- EPPlus/Drawing/Chart/ExcelChart.cs | 5 +- EPPlus/Drawing/Chart/ExcelChartErrorBar.cs | 81 ++++++++++++++++-- EPPlus/Drawing/Chart/ExcelChartSerie.cs | 35 +++++++- EPPlus/Drawing/Chart/ExcelChartSeries.cs | 10 ++- EPPlus/Drawing/Chart/ExcelLineChartSerie.cs | 43 +++++++++- .../Drawing/Chart/ExcelScatterChartSerie.cs | 85 ++++++++++++++++++- SampleApp/Sample_ErrorBars.cs | 53 ++++++++++++ 8 files changed, 331 insertions(+), 24 deletions(-) diff --git a/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs b/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs index 9c02788e6..a92383d02 100644 --- a/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs +++ b/EPPlus/Drawing/Chart/ExcelBarChartSerie.cs @@ -26,8 +26,9 @@ * * Author Change Date * ****************************************************************************** - * Jan Källman Initial Release 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Jan Källman Initial Release 2009-10-01 + * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Kris Wragg Added error bar functionality 2019-08-25 *******************************************************************************/ using System; using System.Collections.Generic; @@ -80,19 +81,43 @@ internal bool InvertIfNegative } } - const string _errBarPath = "c:errBars"; - - ExcelChartErrorBar _errorBar = null; + /// + /// Returns the error bar or creates it if it does not exist + /// public ExcelChartErrorBar ErrorBar { get { - if (_errorBar == null) + if (HasErrorBar == false) { - var node = CreateNode(_errBarPath); - _errorBar = new ExcelChartErrorBar(this, NameSpaceManager, node); + _horizontalErrorBar = AddErrorBar(); } - return _errorBar; + + return _horizontalErrorBar; + } + } + + /// + /// Returns whether this series has an error bar associated with it + /// + public bool HasErrorBar + { + get + { + return _horizontalErrorBar != null; + } + } + + /// + /// Deletes the associated error bar if it exists + /// + public void DeleteErrorBar() + { + if(HasErrorBar) + { + ExcelChartErrorBar errBar = _horizontalErrorBar; + errBar.TopNode.ParentNode.RemoveChild(errBar.TopNode); + _horizontalErrorBar = null; } } } diff --git a/EPPlus/Drawing/Chart/ExcelChart.cs b/EPPlus/Drawing/Chart/ExcelChart.cs index 7cf9251a1..0cd2c7c7a 100644 --- a/EPPlus/Drawing/Chart/ExcelChart.cs +++ b/EPPlus/Drawing/Chart/ExcelChart.cs @@ -26,8 +26,9 @@ * * Author Change Date ******************************************************************************* - * Jan Källman Added 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Jan Källman Added 2009-10-01 + * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Kris Wragg Added enums for error bars 2019-08-25 *******************************************************************************/ using System; using System.Collections.Generic; diff --git a/EPPlus/Drawing/Chart/ExcelChartErrorBar.cs b/EPPlus/Drawing/Chart/ExcelChartErrorBar.cs index 9fe317ca1..8961eaa05 100644 --- a/EPPlus/Drawing/Chart/ExcelChartErrorBar.cs +++ b/EPPlus/Drawing/Chart/ExcelChartErrorBar.cs @@ -1,8 +1,36 @@ -using System; -using System.Collections.Generic; +/******************************************************************************* + * 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 https://github.com/JanKallman/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 + * ****************************************************************************** + * Kris Wragg Initial Release 2019-08-25 + *******************************************************************************/ + +using System; using System.Globalization; -using System.Linq; -using System.Text; using System.Xml; namespace OfficeOpenXml.Drawing.Chart @@ -13,9 +41,11 @@ public class ExcelChartErrorBar : XmlHelper protected XmlNode _node; protected XmlNamespaceManager _ns; + const string BARDIRPATH = "c:errDir/@val"; const string BARTYPEPATH = "c:errBarType/@val"; const string VALTYPEPATH = "c:errValType/@val"; const string NOENDCAPVALUEPATH = "c:noEndCap/@val"; + const string _errorBarValuePath = "c:val/@val"; const string _minusErrorPath = "c:minus/c:numRef/c:f"; const string _plusErrorPath = "c:plus/c:numRef/c:f"; @@ -34,6 +64,47 @@ internal ExcelChartErrorBar(ExcelChartSerie chartSerie, XmlNamespaceManager ns, SchemaNodeOrder = new string[] { "errDir", "errBarType", "errValType", "noEndCap", "plus", "minus", "val", "spPr" }; } + internal ExcelChartErrorBar(ExcelChartSerie chartSerie, XmlNamespaceManager ns, XmlNode node, eErrorBarDirection direction) + : this(chartSerie, ns, node) + { + Direction = direction; + } + + /// + /// The direction of the error bar - X or Y. + /// + public eErrorBarDirection Direction + { + get + { + switch (GetXmlNodeString(BARDIRPATH).ToLower(CultureInfo.InvariantCulture)) + { + case "x": + return eErrorBarDirection.X; + case "y": + return eErrorBarDirection.Y; + default: + return eErrorBarDirection.X; + } + } + + internal set + { + switch (value) + { + case eErrorBarDirection.X: + SetXmlNodeString(BARDIRPATH, "x"); + break; + case eErrorBarDirection.Y: + SetXmlNodeString(BARDIRPATH, "y"); + break; + default: + SetXmlNodeString(BARDIRPATH, "x"); + break; + } + } + } + /// /// The type of the error bars - positive, negative, or both. /// @@ -125,7 +196,7 @@ public eErrorBarValueType ValueType } /// - /// This element specifies an end cap is not drawn on the error bars. + /// This element specifies whether an end cap is not drawn on the error bars. /// public bool NoEndCap { diff --git a/EPPlus/Drawing/Chart/ExcelChartSerie.cs b/EPPlus/Drawing/Chart/ExcelChartSerie.cs index c35d1c5b8..69d09624e 100644 --- a/EPPlus/Drawing/Chart/ExcelChartSerie.cs +++ b/EPPlus/Drawing/Chart/ExcelChartSerie.cs @@ -26,8 +26,9 @@ * * Author Change Date ******************************************************************************* - * Jan Källman Added 2009-12-30 - * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Jan Källman Added 2009-12-30 + * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Kris Wragg Added error bar functionality 2019-08-25 *******************************************************************************/ using System; using System.Collections.Generic; @@ -88,6 +89,16 @@ internal ExcelChartSerie(ExcelChartSeries chartSeries, XmlNamespaceManager ns, X { _xSeriesPath = np; } + + foreach (XmlNode n in node.SelectNodes("c:errBars", ns)) + { + var errBar = new ExcelChartErrorBar(this, ns, n); + + if (errBar.Direction == eErrorBarDirection.Y) // must have been explicitly set as X is the default + _verticalErrorBar = errBar; + else + _horizontalErrorBar = errBar; + } } internal void SetID(string id) { @@ -268,5 +279,25 @@ public ExcelDrawingBorder Border return _border; } } + + protected ExcelChartErrorBar _horizontalErrorBar; + protected ExcelChartErrorBar _verticalErrorBar; + + protected ExcelChartErrorBar AddErrorBar() + { + var errBarNode = _node.OwnerDocument.CreateElement("errBars", ExcelPackage.schemaChart); + XmlNodeList nodes = _node.SelectNodes("c:errBars", _ns); + + if (nodes.Count > 0) + { + _node.InsertAfter(errBarNode, nodes[nodes.Count - 1]); + } + else + { + InserAfter(_node, "c:dLbls,c:invertIfNegative,c:explosion,c:trendline,c:tx,c:marker,c:spPr,c:order,c:idx", errBarNode); + } + + return new ExcelChartErrorBar(this, _ns, errBarNode); + } } } diff --git a/EPPlus/Drawing/Chart/ExcelChartSeries.cs b/EPPlus/Drawing/Chart/ExcelChartSeries.cs index d4e222727..67dd90110 100644 --- a/EPPlus/Drawing/Chart/ExcelChartSeries.cs +++ b/EPPlus/Drawing/Chart/ExcelChartSeries.cs @@ -26,8 +26,10 @@ * * Author Change Date ******************************************************************************* - * Jan Källman Added 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Jan Källman Added 2009-10-01 + * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Kris Wragg Fixed bug where bar charts do not get 2019-08-25 + * ExcelBarChartSerie class type *******************************************************************************/ using System; using System.Collections.Generic; @@ -92,6 +94,10 @@ internal ExcelChartSeries(ExcelChart chart, XmlNamespaceManager ns, XmlNode node { s = new ExcelBubbleChartSerie(this, ns, n, isPivot); } + else if(chart.ChartNode.LocalName == "barChart") + { + s = new ExcelBarChartSerie(this, ns, n, isPivot); + } else { s = new ExcelChartSerie(this, ns, n, isPivot); diff --git a/EPPlus/Drawing/Chart/ExcelLineChartSerie.cs b/EPPlus/Drawing/Chart/ExcelLineChartSerie.cs index ce99acf6c..364de86f7 100644 --- a/EPPlus/Drawing/Chart/ExcelLineChartSerie.cs +++ b/EPPlus/Drawing/Chart/ExcelLineChartSerie.cs @@ -26,8 +26,9 @@ * * Author Change Date * ****************************************************************************** - * Jan Källman Initial Release 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Jan Källman Initial Release 2009-10-01 + * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Kris Wragg Added error bar functionality 2019-08-25 *******************************************************************************/ using System; using System.Collections.Generic; @@ -228,6 +229,44 @@ public Color MarkerLineColor } } + /// + /// Returns the error bar or creates it if it does not exist + /// + public ExcelChartErrorBar ErrorBar + { + get + { + if (HasErrorBar == false) + { + _horizontalErrorBar = AddErrorBar(); + } + + return _horizontalErrorBar; + } + } + + /// + /// Returns whether this series has an error bar associated with it + /// + public bool HasErrorBar + { + get + { + return _horizontalErrorBar != null; + } + } + /// + /// Deletes the associated error bar if it exists + /// + public void DeleteErrorBar() + { + if (HasErrorBar) + { + ExcelChartErrorBar errBar = _horizontalErrorBar; + errBar.TopNode.ParentNode.RemoveChild(errBar.TopNode); + _horizontalErrorBar = null; + } + } } } diff --git a/EPPlus/Drawing/Chart/ExcelScatterChartSerie.cs b/EPPlus/Drawing/Chart/ExcelScatterChartSerie.cs index 598613c4f..9301d3710 100644 --- a/EPPlus/Drawing/Chart/ExcelScatterChartSerie.cs +++ b/EPPlus/Drawing/Chart/ExcelScatterChartSerie.cs @@ -26,8 +26,9 @@ * * Author Change Date * ****************************************************************************** - * Jan Källman Initial Release 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Jan Källman Initial Release 2009-10-01 + * Jan Källman License changed GPL-->LGPL 2011-12-16 + * Kris Wragg Added error bar functionality 2019-08-25 *******************************************************************************/ using System; using System.Collections.Generic; @@ -374,6 +375,86 @@ private string xPath4Alpha(string xPath) return s; } + /// + /// Returns the horizontal error bar or creates it if it does not exist + /// + public ExcelChartErrorBar HorizontalErrorBar + { + get + { + if (HasHorizontalErrorBar == false) + { + _horizontalErrorBar = AddErrorBar(); + _horizontalErrorBar.Direction = eErrorBarDirection.X; + } + + return _horizontalErrorBar; + } + } + + /// + /// Returns whether this series has a horizontal error bar associated with it + /// + public bool HasHorizontalErrorBar + { + get + { + return _horizontalErrorBar != null; + } + } + + /// + /// Deletes the associated horizontal error bar if it exists + /// + public void DeleteHorizontalErrorBar() + { + if (HasHorizontalErrorBar) + { + ExcelChartErrorBar errBar = _horizontalErrorBar; + errBar.TopNode.ParentNode.RemoveChild(errBar.TopNode); + _horizontalErrorBar = null; + } + } + /// + /// Returns the vertical error bar or creates it if it does not exist + /// + public ExcelChartErrorBar VerticalErrorBar + { + get + { + if (HasVerticalErrorBar == false) + { + _verticalErrorBar = AddErrorBar(); + _verticalErrorBar.Direction = eErrorBarDirection.Y; + } + + return _verticalErrorBar; + } + } + + /// + /// Returns whether this series has a vertical error bar associated with it + /// + public bool HasVerticalErrorBar + { + get + { + return _verticalErrorBar != null; + } + } + + /// + /// Deletes the associated vertical error bar if it exists + /// + public void DeleteVerticalErrorBar() + { + if (HasVerticalErrorBar) + { + ExcelChartErrorBar errBar = _verticalErrorBar; + errBar.TopNode.ParentNode.RemoveChild(errBar.TopNode); + _verticalErrorBar = null; + } + } } } diff --git a/SampleApp/Sample_ErrorBars.cs b/SampleApp/Sample_ErrorBars.cs index 1e58873b6..72fba0bce 100644 --- a/SampleApp/Sample_ErrorBars.cs +++ b/SampleApp/Sample_ErrorBars.cs @@ -1,4 +1,5 @@ using OfficeOpenXml; +using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Chart; namespace EPPlusSamples @@ -50,11 +51,63 @@ public static void RunSample_ErrorBars() barSeries.ErrorBar.Value = 10; barSeries.ErrorBar.NoEndCap = false; } + #endregion + + #region Add XY chart + { + var scatterChart = (ExcelScatterChart)ws.Drawings.AddChart("XY1", eChartType.XYScatter); + var scatterSeries = (ExcelScatterChartSerie)scatterChart.Series.Add(ExcelCellBase.GetAddress(2, 3, 6, 3), ExcelCellBase.GetAddress(2, 2, 6, 2)); + scatterChart.Style = eChartStyle.Style2; + scatterChart.SetPosition(30, 0, 0, 0); + + scatterSeries.HorizontalErrorBar.Type = eErrorBarType.Both; + scatterSeries.HorizontalErrorBar.ValueType = eErrorBarValueType.CustomErrorBars; + scatterSeries.HorizontalErrorBar.NoEndCap = false; + scatterSeries.HorizontalErrorBar.PlusAddress = "D2:D6"; + scatterSeries.HorizontalErrorBar.MinusAddress = "E2:E6"; + + scatterSeries.VerticalErrorBar.Type = eErrorBarType.Both; + scatterSeries.VerticalErrorBar.ValueType = eErrorBarValueType.CustomErrorBars; + scatterSeries.VerticalErrorBar.NoEndCap = false; + scatterSeries.VerticalErrorBar.PlusAddress = "F2:F6"; + scatterSeries.VerticalErrorBar.MinusAddress = "G2:G6"; + } + #endregion + + #region Add line chart + { + var lineChart = (ExcelLineChart)ws.Drawings.AddChart("Line1", eChartType.Line); + var lineSeries1 = (ExcelLineChartSerie)lineChart.Series.Add(ExcelCellBase.GetAddress(2, 2, 6, 2), ExcelCellBase.GetAddress(2, 1, 6, 1)); + var lineSeries2 = (ExcelLineChartSerie)lineChart.Series.Add(ExcelCellBase.GetAddress(2, 3, 6, 3), ExcelCellBase.GetAddress(2, 1, 6, 1)); + lineChart.Style = eChartStyle.Style2; + lineChart.SetPosition(41, 0, 0, 0); + + lineSeries1.ErrorBar.Type = eErrorBarType.Plus; + lineSeries1.ErrorBar.ValueType = eErrorBarValueType.FixedValue; + lineSeries1.ErrorBar.NoEndCap = true; + lineSeries1.ErrorBar.Value = 2; + + lineSeries2.ErrorBar.Type = eErrorBarType.Minus; + lineSeries2.ErrorBar.ValueType = eErrorBarValueType.StandardError; + lineSeries2.ErrorBar.NoEndCap = false; + } #endregion package.SaveAs(Utils.GetFileInfo("Sample_ErrorBars.xlsx")); } + + // Load the file back in and make some changes + using (var package = new ExcelPackage(Utils.GetFileInfo("Sample_ErrorBars.xlsx", false))) + { + var ws = package.Workbook.Worksheets[1]; + + var columnChart = (ExcelBarChart)ws.Drawings["ColumnChart1"]; + var columnSeries = columnChart.Series[0] as ExcelBarChartSerie; + columnSeries.ErrorBar.Line.Fill.Color = System.Drawing.Color.Red; + + package.SaveAs(Utils.GetFileInfo("Sample_ErrorBars.xlsx")); + } } } }