Initial commit of the ClosedXML library

Establishes the foundational structure, core Excel functionality, and tooling.

- Implements base components for cells, ranges, columns, and rows.
- Integrates a custom calculation engine with a wide range of Excel functions.
- Adds comprehensive support for styling, conditional formatting, data validation, comments, pictures, and charts.
- Configures build setup, project metadata (NuGet), and developer guidelines.
- Includes editor and Git attributes for consistent code style and line endings.
This commit is contained in:
Thom Lamb
2026-06-23 11:02:53 -05:00
parent 28bc05cf54
commit 2cd6df6481
1092 changed files with 97270 additions and 408 deletions
@@ -0,0 +1,111 @@
using ClosedXML.Attributes;
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace ClosedXML_Examples.Tables
{
public class InsertingTables : IXLExample
{
#region Methods
// Public
public void Create(String filePath)
{
using (var wb = new XLWorkbook())
{
var ws = wb.Worksheets.Add("Inserting Tables");
// From a list of strings
var listOfStrings = new List<String>();
listOfStrings.Add("House");
listOfStrings.Add("Car");
ws.Cell(1, 1).Value = "From Strings";
ws.Cell(1, 1).AsRange().AddToNamed("Titles");
ws.Cell(2, 1).InsertTable(listOfStrings);
// From a list of arrays
var listOfArr = new List<Int32[]>();
listOfArr.Add(new Int32[] { 1, 2, 3 });
listOfArr.Add(new Int32[] { 1 });
listOfArr.Add(new Int32[] { 1, 2, 3, 4, 5, 6 });
ws.Cell(1, 3).Value = "From Arrays";
ws.Range(1, 3, 1, 8).Merge().AddToNamed("Titles");
ws.Cell(2, 3).InsertTable(listOfArr);
// From a DataTable
var dataTable = GetTable();
ws.Cell(7, 1).Value = "From DataTable";
ws.Range(7, 1, 7, 4).Merge().AddToNamed("Titles");
ws.Cell(8, 1).InsertTable(dataTable);
// From a query
var list = new List<Person>();
list.Add(new Person() { Name = "John", Age = 30, House = "On Elm St." });
list.Add(new Person() { Name = "Mary", Age = 15, House = "On Main St." });
list.Add(new Person() { Name = "Luis", Age = 21, House = "On 23rd St." });
list.Add(new Person() { Name = "Henry", Age = 45, House = "On 5th Ave." });
var people = from p in list
where p.Age >= 21
select p;
ws.Cell(7, 6).Value = "From Query";
ws.Range(7, 6, 7, 9).Merge().AddToNamed("Titles");
ws.Cell(8, 6).InsertTable(people);
ws.Cell(15, 6).Value = "From List";
ws.Range(15, 6, 15, 9).Merge().AddToNamed("Titles");
ws.Cell(16, 6).InsertTable(people);
// Prepare the style for the titles
var titlesStyle = wb.Style;
titlesStyle.Font.Bold = true;
titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
titlesStyle.Fill.BackgroundColor = XLColor.Cyan;
// Format all titles in one shot
wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle;
ws.Columns().AdjustToContents();
wb.SaveAs(filePath);
}
}
private class Person
{
[XLColumn(Header = "House Street")]
public String House { get; set; }
public String Name { get; set; }
public Int32 Age { get; set; }
[XLColumn(Header = "Class Type")]
public static String ClassType { get { return nameof(Person); } }
}
// Private
private DataTable GetTable()
{
DataTable table = new DataTable();
table.Columns.Add("Dosage", typeof(int));
table.Columns.Add("Drug", typeof(string));
table.Columns.Add("Patient", typeof(string));
table.Columns.Add("Date", typeof(DateTime));
table.Rows.Add(25, "Indocin", "David", new DateTime(2000, 1, 1));
table.Rows.Add(50, "Enebrel", "Sam", new DateTime(2000, 1, 2));
table.Rows.Add(10, "Hydralazine", "Christoff", new DateTime(2000, 1, 3));
table.Rows.Add(21, "Combivent", "Janet", new DateTime(2000, 1, 4));
table.Rows.Add(100, "Dilantin", "Melanie", new DateTime(2000, 1, 5));
return table;
}
// Override
#endregion Methods
}
}
@@ -0,0 +1,48 @@
using ClosedXML.Excel;
using MoreLinq;
using System;
using System.Linq;
// TODO: Add example to Wiki
namespace ClosedXML_Examples.Tables
{
public class ResizingTables : IXLExample
{
public void Create(string filePath)
{
using (var wb = new XLWorkbook())
{
var ws1 = wb.AddWorksheet("Sheet1");
var data1 = Enumerable.Range(1, 10)
.Select(i =>
new
{
Index = i,
Character = Convert.ToChar(64 + i),
String = new String('a', i),
Integer = 64 + i
});
var table1 = ws1.Cell("B2").InsertTable(data1, true)
.SetShowHeaderRow()
.SetShowTotalsRow();
table1.Fields.First().TotalsRowLabel = "Sum of Integer";
table1.Fields.Last().TotalsRowFunction = XLTotalsRowFunction.Sum;
var ws2 = ws1.CopyTo("Sheet2");
var table2 = ws2.Tables.First();
table2.Resize(table2.FirstCell(), table2.LastCell().CellLeft().CellAbove(3));
var ws3 = ws2.CopyTo("Sheet3");
var table3 = ws3.Tables.First();
table3.Resize(table3.FirstCell().CellLeft(), table3.LastCell().CellRight().CellBelow(1));
wb.Worksheets.ForEach(ws => ws.Columns().AdjustToContents());
wb.SaveAs(filePath);
}
}
}
}
+116
View File
@@ -0,0 +1,116 @@
using ClosedXML.Excel;
using System;
using System.IO;
namespace ClosedXML_Examples.Tables
{
public class UsingTables : IXLExample
{
#region Methods
// Public
public void Create(String filePath)
{
string tempFile = ExampleHelper.GetTempFilePath(filePath);
try
{
new BasicTable().Create(tempFile);
using (var wb = new XLWorkbook(tempFile))
{
var ws = wb.Worksheet(1);
ws.Name = "Contacts Table";
var firstCell = ws.FirstCellUsed();
var lastCell = ws.LastCellUsed();
var range = ws.Range(firstCell.Address, lastCell.CellRight().Address);
range.FirstRow().Delete(); // Deleting the "Contacts" header (we don't need it for our purposes)
// We want to use a theme for table, not the hard coded format of the BasicTable
range.Clear(XLClearOptions.AllFormats);
// Put back the date and number formats
range.Column(4).Style.NumberFormat.NumberFormatId = 15;
range.Column(5).Style.NumberFormat.Format = "$ #,##0";
// Add a field
range.Column(6).FirstCell().SetValue("Age");
var c = range.Column(6).FirstCell().CellBelow();
c.Style.NumberFormat.SetFormat("0.00");
c.FormulaA1 = "=(DATE(2017, 10, 3) - E3) / 365";
c.CopyTo(c.CellBelow())
.CopyTo(c.CellBelow().CellBelow());
var table = range.CreateTable(); // You can also use range.AsTable() if you want to
// manipulate the range as a table but don't want
// to create the table in the worksheet.
// Let's activate the Totals row and add the sum of Income
table.ShowTotalsRow = true;
table.Field("Income").TotalsRowFunction = XLTotalsRowFunction.Sum;
// Just for fun let's add the text "Sum Of Income" to the totals row
table.Field(0).TotalsRowLabel = "Sum Of Income";
table.Field("Age").TotalsRowFunction = XLTotalsRowFunction.Average;
// Copy all the headers
Int32 columnWithHeaders = lastCell.Address.ColumnNumber + 3;
Int32 currentRow = table.RangeAddress.FirstAddress.RowNumber;
ws.Cell(currentRow, columnWithHeaders).Value = "Table Headers";
foreach (var cell in table.HeadersRow().Cells())
{
currentRow++;
ws.Cell(currentRow, columnWithHeaders).Value = cell.Value;
}
// Format the headers as a table with a different style and no autofilters
var htFirstCell = ws.Cell(table.RangeAddress.FirstAddress.RowNumber, columnWithHeaders);
var htLastCell = ws.Cell(currentRow, columnWithHeaders);
var headersTable = ws.Range(htFirstCell, htLastCell).CreateTable("Headers");
headersTable.Theme = XLTableTheme.TableStyleLight10;
headersTable.ShowAutoFilter = false;
// Add a custom formula to the headersTable
headersTable.ShowTotalsRow = true;
headersTable.Field(0).TotalsRowFormulaA1 = "CONCATENATE(\"Count: \", CountA(Headers[Table Headers]))";
// Copy the names
Int32 columnWithNames = columnWithHeaders + 2;
currentRow = table.RangeAddress.FirstAddress.RowNumber; // reset the currentRow
ws.Cell(currentRow, columnWithNames).Value = "Names";
foreach (var row in table.DataRange.Rows())
{
currentRow++;
var fName = row.Field("FName").GetString(); // Notice how we're calling the cell by field name
var lName = row.Field("LName").GetString(); // Notice how we're calling the cell by field name
var name = String.Format("{0} {1}", fName, lName);
ws.Cell(currentRow, columnWithNames).Value = name;
}
// Format the names as a table with a different style and no autofilters
var ntFirstCell = ws.Cell(table.RangeAddress.FirstAddress.RowNumber, columnWithNames);
var ntLastCell = ws.Cell(currentRow, columnWithNames);
var namesTable = ws.Range(ntFirstCell, ntLastCell).CreateTable();
namesTable.Theme = XLTableTheme.TableStyleLight12;
namesTable.ShowAutoFilter = false;
ws.Columns().AdjustToContents();
ws.Columns("A,H,J").Width = 3;
wb.SaveAs(filePath);
}
}
finally
{
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
}
// Private
// Override
#endregion Methods
}
}