United Kingdom: +44 (0)208 088 8978

Using ClosedXML with F#

ClosedXML is a popular open-source library for reading, manipulating and writing Excel 2007+ (.xlsx, .xlsm) files in .NET applications

We're hiring Software Developers

Click here to find out more

What is ClosedXML

ClosedXML is a popular open-source library for library for reading, manipulating and writing Excel 2007+ (.xlsx, .xlsm) files in .NET applications. It aims to provide an intuitive and user-friendly interface to dealing with the underlying OpenXML API. In this blog post, we'll take a closer look at ClosedXML and provide some code samples to help you get started.

Git: https://github.com/ClosedXML/ClosedXML
Documentation: https://closedxml.readthedocs.io/en/latest/
Wiki: https://github.com/closedxml/closedxml/wiki

Getting Started with ClosedXML

Let's start with a basic example. Here's an example of how to create a new Excel file and add some data to it:

open ClosedXML.Excel

// Create a new workbook
let workbook = new XLWorkbook()

// Add a new worksheet
let worksheet = workbook.Worksheets.Add("Sheet1")

// Add some data to the worksheet
worksheet.Cell("A1").Value <- "Name"
worksheet.Cell("B1").Value <- "Age"
worksheet.Cell("A2").Value <- "John"
worksheet.Cell("B2").Value <- 30

// Save the workbook to a file
workbook.SaveAs("example.xlsx")

In this example, we're creating a new workbook, adding a new worksheet to it, and then adding some data to the worksheet. We're then saving the workbook to a file named "example.xlsx".

Formatting Cells with ClosedXML

ClosedXML also provides a wide range of formatting options, including font styles, background colours, borders, and more. Here's an example of how to format cells in a worksheet:

// Set the font style for the header row
worksheet.Range("A1:B1").Style.Font.Bold <- true

// Set the background colour for the header row
worksheet.Range("A1:B1").Style.Fill.BackgroundColor <- XLColor.LightBlue

// Add borders to the cells
let tableRangeBorderStyle = worksheet.Range("A1:B2").Style.Border

tableRangeBorderStyle.OutsideBorder <- XLBorderStyleValues.Thin
tableRangeBorderStyle.TopBorder <- XLBorderStyleValues.Thin
tableRangeBorderStyle.BottomBorder <- XLBorderStyleValues.Thin
tableRangeBorderStyle.LeftBorder <- XLBorderStyleValues.Thin
tableRangeBorderStyle.RightBorder <- XLBorderStyleValues.Thin

// Save the workbook to a file
workbook.SaveAs("example.xlsx");

In this example, we're setting the font style and background colour for the header row, and adding borders to the cells.

Working with Formulas in ClosedXML

ClosedXML also provides support for working with formulas in Excel. Here's an example of how to use formulas in a worksheet:

// Add some data to the worksheet
worksheet.Cell("A1").Value <- "Quantity"
worksheet.Cell("B1").Value <- "Price"
worksheet.Cell("C1").Value <- "Total"
worksheet.Cell("A2").Value <- 5
worksheet.Cell("B2").Value <- 10

// Add a formula to calculate the total
worksheet.Cell("C2").FormulaA1 <- "=A2*B2"

// Save the workbook to a file
workbook.SaveAs("example.xlsx")

In this example, we're adding some data to the worksheet and then adding a formula to calculate the total. The formula is using the values in cells A2 and B2 to calculate the product, which is then displayed in cell C2.

Making it functional

The downside of ClosedXML is that it is an OOP library that uses mutable types. However, there is a library Built on top of ClosedXML by Zaid Ajaj - ClosedXML.SimpleSheets.

Here's an example of using SimpleSheets:

open ClosedXML
open ClosedXML.Excel
open ClosedXML.SimpleSheets

type User = { Name: string; Age: int }

let users = [
    { Name = "Jane"; Age = 26 }
    { Name = "John"; Age = 25 }
]

let excelFile = Excel.createFrom(users, [
    Excel.field(fun user -> user.Name)
        .header("Name")
        .headerBackgroundColor(XLColor.DarkCyan)
        .headerFontColor(XLColor.White)

    Excel.field(fun user -> user.Age)
        .header("Age")
        .headerBackgroundColor(XLColor.DarkCyan)
        .headerFontColor(XLColor.White)
])

System.IO.File.WriteAllBytes("Users.xlsx", excelFile)

An alternative to that is creating your own helper functions:

let leftBorder color width (cell : IXLCell) =
    cell.Style.Border.LeftBorder <- width
    cell.Style.Border.LeftBorderColor <- color
    cell

let rightBorder color width (cell : IXLCell) =
    cell.Style.Border.RightBorder <- width
    cell.Style.Border.RightBorderColor <- color
    cell

let topBorder color width (cell : IXLCell) =
    cell.Style.Border.TopBorder <- width
    cell.Style.Border.TopBorderColor <- color
    cell

let bottomBorder color width (cell : IXLCell) =
    cell.Style.Border.BottomBorder <- width
    cell.Style.Border.BottomBorderColor <- color
    cell

let border color width =
    bottomBorder color width
    >> leftBorder color width
    >> topBorder color width
    >> rightBorder color width

This allows us to add a border by doing:

worksheet.Cell("C2")
|> border (XLColor.FromHtml "#BFBFBF") XLBorderStyleValues.Thin 
|> ignore

Conclusion

ClosedXML is a powerful and flexible library for creating and manipulating Excel files in .NET applications. Its simple and intuitive API, performance, and wide range of features make it a good choice for working with Excel files programmatically.