Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a gradient fill (GradientFill) with ClosedXML

I'm working on a C#/ASP.NET web thing and want to do an Excel export. First I found OpenXML, made some first steps, but it's really hard to use. Now, I'm using ClosedXML and it's quite a relief - so far.

I came up with the problem to have a gradient fill on a table cell ... no problem with normal fills like

worksheet.Cell(1,1).Style.Fill.SetBackgroundColor(XLColor.Red);

or with Patterns like

worksheet.Cell(1,1).Style.Fill.PatternType = XLFillPatternValues.LightHorizontal;
worksheet.Cell(1,1).Style.Fill.PatternColor = XLColor.Green;
worksheet.Cell(1,1).Style.Fill.PatternBackgroundColor = XLColor.White;

But how to do a GradientFill? As far as I understand ClosedXML is built on the OpenXML SDK, so it should be possible to do a GradientFill - something like (I couldn't test that yet)

GradientFill gFillEx = new GradientFill();
GradientStop gStop1st = new GradientStop();
gStop1st.Position = 0;
gStop1st.Color = new Color { Rgb = HexBinaryValue.FromString("00FF00") };
GradientStop gStop2nd = new GradientStop();
gStop2nd.Position = 1;
gStop2nd.Color = new Color { Rgb = HexBinaryValue.FromString("FFFFFF") };
gFillEx.InsertAt<GradientStop>(gStop1st, 0);
gFillEx.InsertAt<GradientStop>(gStop2nd, 1);

But how to proceed - how to set this gFillEx as fill style of a cell? I also wouldn't mind to use the xml from the styles.xml file directly somewhere here - if I only knew where to put.

Any hints or helpful ideas? Thanks.

like image 239
outofmind Avatar asked Jul 17 '15 11:07

outofmind


1 Answers

After ClosedXML doesn't know about GradientFill and as I didn't found a way to manipulate the WorkbookStylesPart of OpenXML within ClosedXML, I ended up with the following workaround

First generate your .xlsx in a memory stream

public ActionResult XLSX()
{
  System.IO.Stream spreadsheetStream = new System.IO.MemoryStream();
  XLWorkbook workbook = new XLWorkbook();
  IXLWorksheet worksheet = workbook.Worksheets.Add("GradientFillExample");
  worksheet.Cell(1, 1).SetValue("example").Style.Fill.SetBackgroundColor(XLColor.FromHtml("#08F47B")); // use some unique color
  workbook.SaveAs(spreadsheetStream);

The idea is to use a unique fill color - we're going to replace that fill in the WorkbookStylesPart with the gradient fill that we intended to have ... so we open the memory stream with OpenXML again (with the memory stream we don't have to care about a temporary file) and navigate to the stylesheet

  SpreadsheetDocument package = SpreadsheetDocument.Open(spreadsheetStream, true);
  WorkbookPart wbPart = package.GetPartsOfType<WorkbookPart>().FirstOrDefault();
  WorkbookStylesPart wbStylePart = wbPart.GetPartsOfType<WorkbookStylesPart>().FirstOrDefault();
  Stylesheet stylesheet = wbStylePart.Stylesheet; // all three are not null - check if you want

As there are always two default fill styles, the stylesheet's never empty. Now we can search for our unique color in the Fills of the Stylesheet and replace that fill with our gradient fill

  OpenXmlElement oldFill = stylesheet.Fills.FirstOrDefault(f => f.OuterXml.Contains("08F47B")); // find the fill that uses your unique color
  if (oldFill != null) // maybe you generate the .xlsx and the "gradient fill" is not always present
  {
    GradientFill gradientFill = new GradientFill() { Degree = 0 };
    gradientFill.Append(new GradientStop() { Position = 0D, Color = new Color() { Rgb = "FF00FF00" } });
    gradientFill.Append(new GradientStop() { Position = 1D, Color = new Color() { Rgb = "FFFFFFFF" } });
    oldFill.ReplaceChild(gradientFill, oldFill.FirstChild); // inside the fill replace the patternFill with your gradientFill
  }
  package.Close();

Finally we can close the memory stream and present it for download ...

  spreadsheetStream.Position = 0;
  return new FileStreamResult(spreadsheetStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "gradfillexample.xlsx" };
}

For testing, simply put that action into your controller. Don't forget you need some usings:

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using ClosedXML.Excel;

The fun part of this: you can also use other degrees than 0, 90, 180, 270 (will result in some kind of diagonal gradient) and you can even use multi-stop gradients, so something like this is also possible

    GradientFill gradientFill = new GradientFill() { Degree = 354 };
    gradientFill.Append(new GradientStop() { Position = 0D, Color = new Color() { Rgb = "FF00FF00" } });
    gradientFill.Append(new GradientStop() { Position = 0.49D, Color = new Color() { Rgb = "FF00FF00" } });
    gradientFill.Append(new GradientStop() { Position = 0.51D, Color = new Color() { Rgb = "FFFFFFFF" } });
    gradientFill.Append(new GradientStop() { Position = 1D, Color = new Color() { Rgb = "FFFFFFFF" } });

While Excel (and Excel Viewer) displays this correctly (the degree value corresponds to a square cell, so that one might be stretched), you can't edit the degree values or multi-stop gradients in Excel, nevertheless, it's nice for generated sheets.

like image 55
outofmind Avatar answered Sep 30 '22 05:09

outofmind