Search code examples
c#excelepplus

Legend color is incorrect in excel chart created using EPPlus


I am wondering if there is a way to manually set the legend entry colors.

I changed the series colors to a custom color, but the legend color is not updating. See image:

Incorrect legend color


Solution

  • Here is an improved version of the code that the OP linked to in the comments. This version you pass in the color you want (instead of it just using random ones) to SetChartPointsColor and it will set all point colors to the same as well as create and entry for the legend:

    public static void SetChartPointsColor(this ExcelChart chart, int serieNumber, Color color)
    {
        var chartXml = chart.ChartXml;
    
        var nsa = chart.WorkSheet.Drawings.NameSpaceManager.LookupNamespace("a");
        var nsuri = chartXml.DocumentElement.NamespaceURI;
    
        var nsm = new XmlNamespaceManager(chartXml.NameTable);
        nsm.AddNamespace("a", nsa);
        nsm.AddNamespace("c", nsuri);
    
        var serieNode = chart.ChartXml.SelectSingleNode(@"c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser[c:idx[@val='" + serieNumber + "']]", nsm);
        var serie = chart.Series[serieNumber];
        var points = serie.Series.Length;
    
        //Add reference to the color for the legend and data points
        var srgbClr = chartXml.CreateNode(XmlNodeType.Element, "srgbClr", nsa);
        var att = chartXml.CreateAttribute("val");
        att.Value = $"{color.R:X2}{color.G:X2}{color.B:X2}";
        srgbClr.Attributes.Append(att);
    
        var solidFill = chartXml.CreateNode(XmlNodeType.Element, "solidFill", nsa);
        solidFill.AppendChild(srgbClr);
    
        var spPr = chartXml.CreateNode(XmlNodeType.Element, "spPr", nsuri);
        spPr.AppendChild(solidFill);
    
        serieNode.AppendChild(spPr);
    }
    

    Use it like this:

    using (var pck = new ExcelPackage(fileInfo))
    {
        var workbook = pck.Workbook;
        var worksheet = workbook.Worksheets.Add("Sheet1");
        worksheet.Cells.LoadFromDataTable(datatable, true);
    
        var chart = worksheet.Drawings.AddChart("chart test", eChartType.ColumnStacked);
        chart.Series.Add(worksheet.Cells["B2:B11"], worksheet.Cells["A2:A11"]);
        chart.Series.Add(worksheet.Cells["C2:C11"], worksheet.Cells["A2:A11"]);
    
        var rand = new Random();
        var color = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256));
    
        chart.SetChartPointsColor(0, color);
        color = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256));
        chart.SetChartPointsColor(1, color);
    
        pck.Save();
    }
    

    Gives this in the output:

    Example Output

    RESPONSE TO COMMENTS

    For a line series it would be a little different:

    public static void SetLineChartColor(this ExcelChart chart, int serieNumber, Color color)
    {
        var chartXml = chart.ChartXml;
    
        var nsa = chart.WorkSheet.Drawings.NameSpaceManager.LookupNamespace("a");
        var nsuri = chartXml.DocumentElement.NamespaceURI;
    
        var nsm = new XmlNamespaceManager(chartXml.NameTable);
        nsm.AddNamespace("a", nsa);
        nsm.AddNamespace("c", nsuri);
    
        var serieNode = chart.ChartXml.SelectSingleNode($@"c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser[c:idx[@val='{serieNumber}']]", nsm);
        var serie = chart.Series[serieNumber];
        var points = serie.Series.Length;
    
        //Add reference to the color for the legend
        var srgbClr = chartXml.CreateNode(XmlNodeType.Element, "srgbClr", nsa);
        var att = chartXml.CreateAttribute("val");
        att.Value = $"{color.R:X2}{color.G:X2}{color.B:X2}";
        srgbClr.Attributes.Append(att);
    
        var solidFill = chartXml.CreateNode(XmlNodeType.Element, "solidFill", nsa);
        solidFill.AppendChild(srgbClr);
    
        var ln = chartXml.CreateNode(XmlNodeType.Element, "ln", nsa);
        ln.AppendChild(solidFill);
    
        var spPr = chartXml.CreateNode(XmlNodeType.Element, "spPr", nsuri);
        spPr.AppendChild(ln);
    
        serieNode.AppendChild(spPr);
    }