SwiftExcel.Pro Pro

SwiftExcel.Pro is the commercial edition. It keeps the same streaming engine and performance profile as the free edition, but adds multi-sheet workbooks, full cell styling (fonts, colors, borders, fills, alignment), number formats (currency, percentage, dates, custom), custom Excel formulas (including hyperlinks), merged cells, auto filters, freeze panes and custom row heights.

Installation

dotnet add package SwiftExcel.Pro

Bring the namespaces into scope:

using SwiftExcel.Pro;
using SwiftExcel.Pro.Styles; // for Style, Color, NumberFormat, FontName, etc.

The library targets netstandard2.0 and depends on SharpCompress.

Licensing & activation

Set your serial number once at process startup, before instantiating any ExcelWriter:

LicenseInfo.SerialNumber = "YOUR-LICENSE-KEY";

Without a valid serial the workbook is still produced, but the first sheet is renamed to "Evaluation Period". See pricing for available plans.

Quick start

The minimal working example from the official sandbox:

LicenseInfo.SerialNumber = "YOUR-LICENSE-KEY";

using (var ew = new ExcelWriter("C:/temp/test.xlsx"))
{
    for (var row = 1; row <= 100; row++)
    {
        for (var col = 1; col <= 10; col++)
        {
            ew.Write($"row:{row}-col:{col}", col, row);
        }
    }
}

Streaming model — what you must know

  • Cells must be written in increasing (row, col) order within a sheet. Going backwards throws SwiftExcelException.
  • col, row, sheetNumber and styleNumber indices are 1-based for cells/rows/cols/sheets and 0-based for styles (style 0 = the implicit default style).
  • When writing to multiple sheets, you must finish (or skip) earlier sheets before moving to a later sheet number. Going back to a lower sheet number throws.
  • One ExcelWriter per file, single-threaded. Not thread-safe.
  • Output is to a file path only (the Free edition has a Stream constructor; the Pro edition currently does not).

API reference

Namespace: SwiftExcel.Pro (styling types in SwiftExcel.Pro.Styles).

ExcelWriter — 8 constructor overloads

Implements IDisposable. All constructors take a file path plus any combination of one/many sheets and one/many styles:

ExcelWriter(string filePath) — single default sheet, default style.

ExcelWriter(string filePath, Sheet sheet) — one configured sheet.

ExcelWriter(string filePath, IList<Sheet> sheets) — multiple sheets, no custom styles.

ExcelWriter(string filePath, Style style) — single sheet, one custom style.

ExcelWriter(string filePath, IList<Style> styles) — single sheet, many custom styles.

ExcelWriter(string filePath, Sheet sheet, Style style)

ExcelWriter(string filePath, Sheet sheet, IList<Style> styles)

ExcelWriter(string filePath, IList<Sheet> sheets, Style style)

ExcelWriter(string filePath, IList<Sheet> sheets, IList<Style> styles) — full control.

Style numbers are 1-based in Write(), with 0 reserved for the implicit no-styling default. So when you pass a single Style to the constructor, you reference it as styleNumber: 1.

Write()

void Write(string value, int col, int row, int sheetNumber = 1, int styleNumber = 0, DataType dataType = DataType.Text)

  • value — the cell value as a string. null is allowed and is escaped safely.
  • col, row — 1-based cell coordinates.
  • sheetNumber — 1-based sheet index. Defaults to 1. Going to a higher number finishes earlier sheets automatically; going to a lower number throws.
  • styleNumber — 1-based index into the styles list passed to the constructor. 0 means "no style".
  • dataTypeDataType.Text (default), DataType.Number, or DataType.CustomFormula.

WriteFormula()

void WriteFormula(FormulaType type, int col, int row, int sourceCol, int sourceRowStart, int sourceRowEnd, int sheetNumber = 1, int styleNumber = 0)

Writes a built-in AVERAGE / COUNT / MAX / SUM over a vertical range in a single column. For arbitrary formulas (any Excel function, multi-cell references, hyperlinks) use Write(...) with DataType.CustomFormula instead.

Save() and Dispose()

void Save()

Finalizes the current sheet, then auto-finishes any sheets you never wrote to (so multi-sheet workbooks always come out structurally valid), closes the ZIP and disposes the underlying file stream.

void Dispose() (inherited)

Calls Save() if not already finalized. Always wrap in using.

DataType (enum)

ValueMeaning
DataType.TextCell is emitted with t="str"; treated as text.
DataType.NumberCell is treated as numeric. Use this with NumberFormat styles for currency, percentage and date display.
DataType.CustomFormulaThe string is written as an Excel formula (inside <f>...</f>). Use for any Excel function: HYPERLINK, IF, VLOOKUP, custom arithmetic, etc.

FormulaType (enum)

ValueExcel function
FormulaType.Average=AVERAGE(range)
FormulaType.Count=COUNT(range)
FormulaType.Max=MAX(range)
FormulaType.Sum=SUM(range)

Sheet

PropertyTypeDescription
NamestringTab name. Truncated to 31 chars and XML-escaped. If null/empty, defaults to sheet 1, sheet 2, …
RightToLeftboolRight-to-left column direction.
ColumnsWidthIList<double>Per-column widths starting at column 1.
RowsHeightIList<double?>Per-row heights. Use null for "default height" entries; non-null values become custom row heights. Index i ⇒ row i+1.
FilterRangeFilterRangeAuto-filter range definition (single object — only one filter range per sheet).
MergeRangesIList<MergeRange>Cell merge ranges.
FreezeTypeFreezeTypeNone (default), Row or Column.
FreezeCountintHow many rows or columns to pin. Used together with FreezeType. Auto-clamped to 1 if out of range.

MergeRange

new MergeRange(int row, int col, int count)

Merges count cells horizontally starting at (col, row). SwiftExcel.Pro validates that merge ranges do not overlap and throws SwiftExcelException(MergeRangeOverlap) if they do.

FilterRange

new FilterRange(int row, int col, int colCount)

Adds Excel's auto-filter dropdowns to colCount consecutive columns starting at (col, row). The filter automatically extends down to the last row of the worksheet.

FreezeType (enum)

ValueMeaning
FreezeType.NoneDefault. No freeze panes.
FreezeType.RowPin the top FreezeCount rows when scrolling vertically.
FreezeType.ColumnPin the leftmost FreezeCount columns when scrolling horizontally.

Style

From SwiftExcel.Pro.Styles. Pass one or many Style objects to the ExcelWriter constructor; reference them in Write() via the 1-based styleNumber argument (0 = no style).

PropertyTypeDefaultDescription
FontNameFontNameCalibriOffice font enum (200+ values, see below).
FontSizeFontSizeFont11Font1Font36, Font48, Font72.
FontColorstring (RGB hex)Color.BlackHex string like "FF0000". Use predefined Color constants or Color.Custom("RRGGBB").
FontStyleBoldboolfalse
FontStyleItalicboolfalse
FontStyleUnderlineboolfalse
FillColorstring (RGB hex)noneSolid fill color.
BorderColorstringColor.BlackColor used by all enabled borders.
BorderTop / Bottom / Left / RightboolfalseToggle each border individually (thin style).
AlignHorizontalAlignHorizontalLeftLeft / Right / Center.
AlignVerticalAlignVerticalBottomBottom / Center / Top.
NumberFormatstringNumberFormat.DefaultExcel format code. Use NumberFormat.Currency, Percentage, or NumberFormat.Custom("...").
WrapTextboolfalseRequired if your value contains \n and you want it displayed multi-line.

Color

Static helper class with hex string constants. Predefined values: Red, DarkRed, Orange, Yellow, LightGreen, Green, LightBlue, Blue, DarkBlue, Purple, Black, White, Grey. For anything else use Color.Custom("RRGGBB").

var style = new Style
{
    FillColor  = Color.LightBlue,
    FontColor  = Color.Custom("3366CC")
};

NumberFormat

Static helper class. The cell's NumberFormat only takes effect on cells written with DataType.Number.

MemberFormat stringExample output
NumberFormat.Currency$#,##0.00$1,250.76
NumberFormat.Percentage#,##0.00%56.12%
NumberFormat.Custom("...")Any Excel format codee.g. dd/mmm/yyyy hh:mm:ss for dates, #,##0.0000 for shares

FontName / FontSize (enums)

FontName contains 200+ Office fonts. Common values: Arial, ArialBlack, ArialNarrow, Calibri, CalibriLight, Cambria, Candara, Consolas, Constantia, Corbel, CourierNew, Garamond, Georgia, Impact, SegoeUI, Tahoma, TimesNewRoman, TrebuchetMS, Verdana, …

FontSize covers Font1Font36 plus Font48 and Font72.

AlignHorizontal / AlignVertical (enums)

AlignHorizontal: Left (default), Right, Center.

AlignVertical: Bottom (default), Center, Top.

LicenseInfo (static)

static string LicenseInfo.SerialNumber

Set this once before instantiating ExcelWriter. Without a valid serial the first sheet is renamed to "Evaluation Period".

Configuration (static)

static bool Configuration.UseEnchancedXmlEscaping = true

When true (default), control characters that are invalid in XML are encoded with Excel's _xHHHH_ syntax. Applies to both DataType.Text and DataType.CustomFormula cells.

Examples

All examples below are taken directly from SwiftExcel.Pro.Sandbox. Add LicenseInfo.SerialNumber = "..." at startup if you have a license.

Data & structure — Basic write

using (var ew = new ExcelWriter("C:/temp/test.xlsx"))
{
    for (var row = 1; row <= 100; row++)
    {
        for (var col = 1; col <= 10; col++)
        {
            ew.Write($"row:{row}-col:{col}", col, row);
        }
    }
}

Data & structure — Sheet configuration

var sheet = new Sheet
{
    Name = "Monthly Report",
    RightToLeft = true,
    ColumnsWidth = new List<double> { 10, 12, 8, 8, 35 }
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", sheet))
{
    ew.Write("data", 1, 1);
}

Data & structure — Multiple sheets

Pass an IList<Sheet> and target a sheet via the sheetNumber argument:

var sheets = new List<Sheet>
{
    new Sheet { Name = "Summary"   },
    new Sheet { Name = "Details"   },
    new Sheet { Name = "Details 3" },
    new Sheet { Name = "Details 4" },
    new Sheet { Name = "Details 5" }
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", sheets))
{
    ew.Write("value for the 1st sheet", 1, 1, 1);
    ew.Write("value for the 2nd sheet", 1, 1, 2);
    ew.Write("value for the 4th sheet", 1, 2, 4);
}

You may skip sheets — sheet 3 above is left empty and is auto-finalized when sheet 4 is started. You may not, however, jump back to a lower sheet number.

Formulas — Built-in Average / Count / Max / Sum

using (var ew = new ExcelWriter("C:/temp/test.xlsx"))
{
    const int col = 1;
    var row = 1;
    for (; row <= 20; row++)
    {
        ew.Write(row.ToString(), col, row, 1, 0, DataType.Number);
    }

    ew.WriteFormula(FormulaType.Average, col, ++row, col, 1, 20);
    ew.WriteFormula(FormulaType.Count,   col, ++row, col, 1, 20);
    ew.WriteFormula(FormulaType.Max,     col, ++row, col, 1, 20);
    ew.WriteFormula(FormulaType.Sum,     col, ++row, col, 1, 20);
}

Formulas — Custom (any Excel function)

Write any Excel formula by passing the formula without the leading = and using DataType.CustomFormula:

using (var ew = new ExcelWriter("C:/temp/test.xlsx"))
{
    const int col = 1;
    var row = 1;
    for (; row <= 20; row++)
    {
        ew.Write(row.ToString(), col, row, 1, 0, DataType.Number);
    }

    ew.Write("A20/A10",        col, ++row, 1, 0, DataType.CustomFormula);
    ew.Write("SUM(A1:A20)-A26", col, ++row, 1, 0, DataType.CustomFormula);
}

Data types — Dates with .ToOADate() (critical pattern)

Excel stores dates as OLE Automation date doubles. To produce a formatted date cell you need both:

  1. A Style with NumberFormat.Custom("dd/mmm/yyyy hh:mm:ss") (or any Excel date format string).
  2. The DateTime value converted via .ToOADate() and written as DataType.Number, with the matching styleNumber.
var style = new Style
{
    NumberFormat = NumberFormat.Custom("dd/mmm/yyyy hh:mm:ss")
};

var dateToExport = new DateTime(2023, 8, 21, 10, 51, 18);
using (var ew = new ExcelWriter("C:/temp/test.xlsx", style))
{
    ew.Write($"{dateToExport.ToOADate()}", 1, 2, 1, 1, DataType.Number);
}

Why this is non-obvious: writing the date as a string looks correct in the cell but breaks Excel's sort, filter and arithmetic features. The OADate conversion produces a real numeric date that Excel can compute on, while the matching number format makes it display the way a human expects.

Number formats — Currency

var style = new Style
{
    FontSize     = FontSize.Font11,
    NumberFormat = NumberFormat.Currency
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", style))
{
    ew.Write("2612.11", 1, 2, 1, 1, DataType.Number);
}

Number formats — Percentage

var style = new Style
{
    FontSize     = FontSize.Font11,
    NumberFormat = NumberFormat.Percentage
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", style))
{
    ew.Write("0.3851", 1, 2, 1, 1, DataType.Number);
}

Number formats — Custom (e.g. shares)

var style = new Style
{
    FontSize     = FontSize.Font11,
    NumberFormat = NumberFormat.Custom("#,##0.0000")
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", style))
{
    ew.Write("1357.5712", 1, 2, 1, 1, DataType.Number);
}

Styling — Fonts & alignment

var style = new Style
{
    FontColor          = Color.Black,
    FontName           = FontName.TimesNewRoman,
    FontSize           = FontSize.Font16,
    FontStyleBold      = true,
    FontStyleItalic    = true,
    FontStyleUnderline = true,
    AlignHorizontal    = AlignHorizontal.Left,
    AlignVertical      = AlignVertical.Center,
    WrapText           = true
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", style))
{
    ew.Write("font with\ndecoration", 1, 2, 1, 1);
}

Note that WrapText = true is required for the \n to render as a real line break.

Styling — Borders & fill colors (multiple styles)

var styles = new List<Style>
{
    new Style
    {
        BorderLeft = true, BorderRight = true,
        BorderTop  = true, BorderBottom = true,
        BorderColor = Color.Black
    },
    new Style { FillColor = Color.Grey }
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", styles))
{
    ew.Write("borders",    2, 2, 1, 1);  // styleNumber: 1
    ew.Write("fill color", 3, 3, 1, 2);  // styleNumber: 2
}

Layout — Merging & auto filter

var sheet = new Sheet
{
    Name = "Monthly Report",
    FilterRange = new FilterRange(1, 1, 5),
    MergeRanges = new List<MergeRange>
    {
        new MergeRange(2, 3, 5),
        new MergeRange(3, 2, 7)
    }
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", sheet))
{
    ew.Write("value 1", 1, 2);
    ew.Write("value 2", 2, 2);
}

The filter sets dropdowns on A1:Elast. The merge ranges merge 5 cells horizontally starting at (col=3, row=2) and 7 cells starting at (col=2, row=3).

Layout — Freeze panes

var sheet = new Sheet
{
    Name        = "Monthly Report",
    FreezeType  = FreezeType.Row,
    FreezeCount = 25
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", sheet))
{
    for (var row = 1; row <= 100; row++)
    {
        for (var col = 1; col <= 10; col++)
        {
            ew.Write($"row:{row}-col:{col}", col, row);
        }
    }
}

Layout — Custom row heights

Use null as a list entry to keep Excel's default for that row; non-null values produce a custom height.

var sheet = new Sheet
{
    RowsHeight = new List<double?> { 0, null, 35 }
};

using (var ew = new ExcelWriter("C:/temp/test.xlsx", sheet))
{
    ew.Write("data", 1, 1);  // hidden (height 0)
    ew.Write("data", 1, 2);  // default height
    ew.Write("data", 1, 3);  // 35pt tall
}

Special values — Invalid XML & reserved characters

Configuration.UseEnchancedXmlEscaping = true;

var sheet = new Sheet { Name = "Monthly\"< Report" };

using (var ew = new ExcelWriter("C:/temp/test.xlsx", sheet))
{
    ew.Write("<",  1, 1);
    ew.Write(">",  2, 1);
    ew.Write("&",  3, 1);
    ew.Write("'",  4, 1);
    ew.Write("\"", 5, 1);
}

Both cell values and sheet names are XML-escaped automatically.

FAQ

Can SwiftExcel.Pro read existing .xlsx files?

No. Like the Free edition, SwiftExcel.Pro is intentionally write-only. It produces .xlsx files but never reads or modifies existing ones.

Does SwiftExcel.Pro support .xls or .xlsm?

No. Only the modern Open XML .xlsx format is produced. There is no support for the legacy binary .xls format or for macro-enabled .xlsm files.

Is ExcelWriter thread-safe?

No. A single ExcelWriter must not be shared across threads. Even with multiple sheets, all writes go through a single underlying ZIP/XML stream that requires strictly ordered output. Parallelize across files (one writer per thread), not within a single workbook.

Which .NET versions are supported?

The package targets netstandard2.0, so it works on .NET Framework 4.6.1+, .NET Core 2.0+, and .NET 5, 6, 7, 8 and 9. The only runtime dependency is SharpCompress.

How do I activate a license?

Set SwiftExcel.Pro.LicenseInfo.SerialNumber = "YOUR-KEY"; once at startup, before instantiating ExcelWriter. Without a valid serial the first sheet is renamed to "Evaluation Period". See pricing for purchase options.

Can I write to a Stream (e.g. Azure Blob)?

The Pro ExcelWriter currently accepts only a file path. For streaming directly to cloud storage use the Free edition's ExcelWriter(Stream) constructor or, for Pro workbooks, generate the file locally and then upload it.

How big a file can SwiftExcel.Pro produce?

Memory consumption is essentially flat regardless of row count, so files with millions of rows across multiple sheets are routine. The hard upper bounds are Excel's own: 1,048,576 rows × 16,384 columns per worksheet.

Why does my custom formula say #NAME? in Excel?

Two common causes: (1) you included a leading = in the formula string — don't (write "SUM(A1:A20)", not "=SUM(A1:A20)"); (2) you used a function name your installed Excel version does not recognize. The library does not validate formula syntax — Excel does, on first open.

Why doesn't \n show as a line break in my cell?

Excel only renders embedded line breaks when wrap-text is on for the cell. Set Style.WrapText = true on the style you apply to that cell.

Why is my second cell on the same row off by one column?

Make sure you are on at least version 1.1.10 of the Pro package — earlier versions had a bug where the column counter was not reset between rows.

Using the free version?

See the SwiftExcel (Free) documentation → — the free package has a simpler API but the same streaming engine and performance characteristics.