How to export collection of data to Xlsx file in C# using ClosedXML

ClosedXML is a fine library for exporting data to Excel. It is very simple to use and fast enough for smaller sets of data. But it also has some cons:

1.) It uses OpenXML – that is not a con by itself, but the problem appears when you want to save file as Xls. XML concept of Office files was presented with Office 2007 and is not compatible with previous versions of Office. But if you really need Xls, you can create Excel file using ClosedXML and than open and save it as Xls using Interop. That is only possible if you are exporting data on client side with installed PIA and Office. It is strongly advised against using Interop on server side due to the fact that Office is client side application!
2.) It is very slow for big datasets (more than few 10 thousand rows). For exporting big datasets you should use OpenXML (maybe I will talk about that in one of my next posts).
3.) On project’s CodePlex site it is written (quote): “ClosedXML makes it easier for developers to create Excel 2007/2010 files.”. But I believe Excel 2013 and newer should also open files generated with ClosedXML (haven’t tested it though).

Firstly you have to add reference to ClosedXML. You can do that using NuGet Package Manager or you can download ClosedXML DLL from CodePlex and add a reference to your project.

NuGet

NuGet Package Manager

You also need to add a namespace to your CS file (using ClosedXML.Excel;).

Using ClosedXML you can set specific cell values, you can insert data from DataTable or from some other collection. In this post I will show you how to create generic function, which inserts data to Excel from IEnumerable of objects. I will pass column titles in separate List of string arrays.


Info: I could probably set column titles using descriptors on object properties – something like this:

[MyAttribute("First property")]
public string FstProperty
{
  get {....}
  set {....}
}

But for the sake of the example I won’t complicate – I will pass the titles to the function in a separate List.


Our function should look something like this:

public byte[] ExportToExcel<T>(IEnumerable<T> data, string worksheetTitle, List<string[]> titles)
{  
  var wb = new XLWorkbook(); //create workbook
  var ws = wb.Worksheets.Add(worksheetTitle); //add worksheet to workbook

  ws.Cell(1, 1).InsertData(titles); //insert titles to first row
            
  if (data != null && data.Count() > 0)
  {
    //insert data to from second row on
    ws.Cell(2, 1).InsertData(data); 
  }

  //save file to memory stream and return it as byte array
  using (var ms = new MemoryStream())
  {
    wb.SaveAs(ms);

    return ms.ToArray();
  }
}

And that is it! If we wish to add some special styling to our heading row, we have to modify our code snippet a little:

var rangeTitle = ws.Cell(1, 1).InsertData(titles); //insert titles to first row
rangeTitle.AddToNamed("Titles");

var titlesStyle = wb.Style;
titlesStyle.Font.Bold = true; //font must be bold
titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; //align text to center
titlesStyle.Fill.BackgroundColor = XLColor.Red; //background must be red

wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle; //attach style to the range

We can also adjust column width to maximum content width using function AdjustToContents right before we save the file:

ws.Columns().AdjustToContents();