Skip to footer content
USING IRONXL

C# Export DataGridView to Excel with Column Headers

How Do You Export a DataGridView to Excel with Column Headers in C#?

Exporting data from a Windows Forms DataGridView control to Excel format is a common requirement, but developers often hit a frustrating wall: the exported file is missing column headers. When you need to export DataGridView to Excel with column headers, the solution must preserve all your data and formatting reliably. Traditional approaches using Microsoft Office Interop can be slow, fragile, and require MS Excel to be installed on every machine that runs the code.

IronXL solves this problem cleanly. It is a .NET Excel library that reads, creates, and writes Excel files without any Office dependency. This tutorial walks you through a complete export solution -- one that handles column headers, data types, null cells, and user-friendly file saving in just a few lines of code.

By the end, you will have a working Windows Forms button handler that exports a DataGridView to an XLSX file with all column headers intact.

What You Need Before Starting

Before writing any code, make sure the following are in place:

  • Visual Studio 2022 or later (any edition)
  • .NET 10 SDK installed
  • A Windows Forms Application project targeting .NET 10
  • NuGet access to install IronXL

The examples below use top-level statements and the modern .NET 10 project structure throughout.

How Do You Install the Library in a .NET Project?

Open the NuGet Package Manager Console in Visual Studio and run either of the following commands. The package is listed on NuGet Gallery and targets .NET Standard 2.0 and above, so it works with any modern .NET project.

Install-Package IronXL
Install-Package IronXL
SHELL
dotnet add package IronXL
dotnet add package IronXL
SHELL

After installation, add the required namespaces at the top of your form file:

using IronXL;
using System;
using System.Data;
using System.Windows.Forms;
using IronXL;
using System;
using System.Data;
using System.Windows.Forms;
$vbLabelText   $csharpLabel

These imports give access to IronXL's Excel functionality, DataTable operations, and the Windows Forms controls needed for the export process.

How Do You Set Up a DataGridView with Sample Data?

Start by creating a Windows Forms Application in Visual Studio. Add a DataGridView named dataGridView1 and a Button named btnExport to the main form through the designer. Then populate the grid on form load:

using System;
using System.Data;
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        DataTable dt = new DataTable();

        // Define columns -- these names become Excel headers
        dt.Columns.Add("Product ID", typeof(int));
        dt.Columns.Add("Product Name", typeof(string));
        dt.Columns.Add("Price", typeof(decimal));
        dt.Columns.Add("Stock Quantity", typeof(int));

        // Add sample rows
        dt.Rows.Add(1001, "Laptop", 999.99m, 15);
        dt.Rows.Add(1002, "Mouse", 29.99m, 50);
        dt.Rows.Add(1003, "Keyboard", 79.99m, 30);
        dt.Rows.Add(1004, "Monitor", 299.99m, 12);
        dt.Rows.Add(1005, "Headphones", 89.99m, 25);

        // Bind data to the grid
        dataGridView1.DataSource = dt;
    }
}
using System;
using System.Data;
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        DataTable dt = new DataTable();

        // Define columns -- these names become Excel headers
        dt.Columns.Add("Product ID", typeof(int));
        dt.Columns.Add("Product Name", typeof(string));
        dt.Columns.Add("Price", typeof(decimal));
        dt.Columns.Add("Stock Quantity", typeof(int));

        // Add sample rows
        dt.Rows.Add(1001, "Laptop", 999.99m, 15);
        dt.Rows.Add(1002, "Mouse", 29.99m, 50);
        dt.Rows.Add(1003, "Keyboard", 79.99m, 30);
        dt.Rows.Add(1004, "Monitor", 299.99m, 12);
        dt.Rows.Add(1005, "Headphones", 89.99m, 25);

        // Bind data to the grid
        dataGridView1.DataSource = dt;
    }
}
$vbLabelText   $csharpLabel

The column names defined in dt.Columns.Add(...) calls will become the header row in the exported Excel file. You can substitute any data source here -- a database query result, a CSV import, or an API response stored as a DataTable all work identically with the export code shown later. The DataTable approach is particularly convenient because DataGridView exposes a DataSource property that accepts any IList or IBindingList, meaning the same export code generalises across every binding scenario without modification.

If your DataGridView is populated from a database using a SqlDataAdapter, the DataTable already contains typed columns. Passing those typed values to SetCellValue rather than calling ToString() on them keeps numeric columns numeric in the Excel output, which matters for any downstream reporting or pivot table usage.

This creates a DataGridView populated with all the data:

C# Export DataGridView to Excel with Column Headers Using IronXL: Image 1 - Sample data in a DataGridView

For more advanced data binding scenarios, Microsoft's documentation on DataGridView data binding provides additional examples.

How Do You Export the DataGridView to Excel While Preserving Column Headers?

The export method reads column headers from the HeaderText property of each DataGridView column and writes them to row 0 of the worksheet. Data rows follow from row 1 onward. Wire this method to the btnExport click event:

private void btnExport_Click(object sender, EventArgs e)
{
    try
    {
        // Create a new Excel workbook in XLSX format
        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Exported Data");

        // Write column headers to row 0
        for (int col = 0; col < dataGridView1.Columns.Count; col++)
        {
            worksheet.SetCellValue(0, col, dataGridView1.Columns[col].HeaderText);
        }

        // Write data rows starting at row 1
        for (int row = 0; row < dataGridView1.Rows.Count; row++)
        {
            // Skip the placeholder new-row at the bottom of the grid
            if (dataGridView1.AllowUserToAddRows && row == dataGridView1.Rows.Count - 1)
                continue;

            for (int col = 0; col < dataGridView1.Columns.Count; col++)
            {
                var cellValue = dataGridView1.Rows[row].Cells[col].Value;
                if (cellValue != null)
                {
                    worksheet.SetCellValue(row + 1, col, cellValue.ToString());
                }
            }
        }

        // Prompt the user to choose a save location
        using SaveFileDialog saveFileDialog = new SaveFileDialog
        {
            Filter = "Excel Files|*.xlsx",
            FileName = "DataGridView_Export.xlsx"
        };

        if (saveFileDialog.ShowDialog() == DialogResult.OK)
        {
            workbook.SaveAs(saveFileDialog.FileName);
            MessageBox.Show("Export completed successfully!", "Success",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Export failed: {ex.Message}", "Error",
                       MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
private void btnExport_Click(object sender, EventArgs e)
{
    try
    {
        // Create a new Excel workbook in XLSX format
        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Exported Data");

        // Write column headers to row 0
        for (int col = 0; col < dataGridView1.Columns.Count; col++)
        {
            worksheet.SetCellValue(0, col, dataGridView1.Columns[col].HeaderText);
        }

        // Write data rows starting at row 1
        for (int row = 0; row < dataGridView1.Rows.Count; row++)
        {
            // Skip the placeholder new-row at the bottom of the grid
            if (dataGridView1.AllowUserToAddRows && row == dataGridView1.Rows.Count - 1)
                continue;

            for (int col = 0; col < dataGridView1.Columns.Count; col++)
            {
                var cellValue = dataGridView1.Rows[row].Cells[col].Value;
                if (cellValue != null)
                {
                    worksheet.SetCellValue(row + 1, col, cellValue.ToString());
                }
            }
        }

        // Prompt the user to choose a save location
        using SaveFileDialog saveFileDialog = new SaveFileDialog
        {
            Filter = "Excel Files|*.xlsx",
            FileName = "DataGridView_Export.xlsx"
        };

        if (saveFileDialog.ShowDialog() == DialogResult.OK)
        {
            workbook.SaveAs(saveFileDialog.FileName);
            MessageBox.Show("Export completed successfully!", "Success",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Export failed: {ex.Message}", "Error",
                       MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
$vbLabelText   $csharpLabel

Breaking Down the Export Steps

Each step of the method serves a specific purpose:

  • WorkBook.Create: Initializes a new Excel file in memory using the XLSX format. No file is written to disk until SaveAs is called.
  • CreateWorkSheet: Adds a named sheet to hold the exported data. The name "Exported Data" appears as the tab label in Excel.
  • Column header loop: Reads dataGridView1.Columns[col].HeaderText for each column and writes it to row index 0. This is the key step that preserves the headers.
  • Data row loops: Nested loops iterate every row and column, using row + 1 as the Excel row index so data always starts below the header row.
  • Null check: Prevents exceptions when a cell contains no value, which is common in real data.
  • SaveFileDialog: Lets users pick the file name and destination at runtime rather than hard-coding a path.

The exported file looks like this:

C# Export DataGridView to Excel with Column Headers Using IronXL: Image 2 - Output Excel file with the exported sample data

How Do You Handle Common Edge Cases During Export?

Real-world data rarely looks as clean as a sample dataset. Here are the scenarios you are most likely to encounter and how to address them.

Empty Cells

The null check (if (cellValue != null)) in the data loop handles empty cells without throwing an exception. Empty cells remain blank in the Excel output, preserving the grid structure.

Mixed Data Types

IronXL handles different data formats automatically. When you pass a number as a string via ToString(), Excel may still recognise it as numeric depending on the value. For precise type control, cast the cell value to the correct type before writing:

if (cellValue is int intVal)
    worksheet.SetCellValue(row + 1, col, intVal);
else if (cellValue is decimal decVal)
    worksheet.SetCellValue(row + 1, col, (double)decVal);
else
    worksheet.SetCellValue(row + 1, col, cellValue?.ToString() ?? string.Empty);
if (cellValue is int intVal)
    worksheet.SetCellValue(row + 1, col, intVal);
else if (cellValue is decimal decVal)
    worksheet.SetCellValue(row + 1, col, (double)decVal);
else
    worksheet.SetCellValue(row + 1, col, cellValue?.ToString() ?? string.Empty);
$vbLabelText   $csharpLabel

This approach keeps numeric columns numeric in Excel so formulas and sorting work correctly downstream.

Special Characters in Headers

Column headers with special characters such as &, <, >, and accented letters export correctly. IronXL handles encoding automatically and does not require any escaping on your part.

Large Datasets

For grids containing tens of thousands of rows, consider moving the export logic to a background thread to keep the UI responsive. Wrap the WorkBook.Create and loop logic in a Task.Run call and update the UI from the completed callback. Microsoft's documentation on async programming with Task explains the pattern in detail.

For additional scenarios such as cell styling, formula preservation, and password protection, the IronXL documentation covers each in detail.

How Do the Main Export Approaches Compare?

Before committing to a library, it helps to understand the trade-offs between the most common methods.

Comparison of DataGridView to Excel export methods in C#
Method Office Required Column Headers Performance Server-Safe
Microsoft Office Interop Yes Manual Slow No
OpenXml SDK No Manual Fast Yes
ClosedXML No Manual Good Yes
IronXL No Automatic Fast Yes

Interop-based solutions require Excel to be installed on the server, which is both a licensing concern and a deployment complexity. OpenXml SDK is powerful but low-level -- writing a header row requires building XML structures manually. IronXL and ClosedXML both provide higher-level APIs, but IronXL's WorkBook and WorkSheet model maps closely to the way developers already think about spreadsheets.

How Do You Export to CSV Instead of XLSX?

The same export pattern applies when you need a CSV file instead of an Excel workbook. Change the file format and extension:

WorkBook csvWorkbook = WorkBook.Create(ExcelFileFormat.CSV);
WorkSheet csvSheet = csvWorkbook.CreateWorkSheet("Data");

// Headers and data loops remain identical

using SaveFileDialog dialog = new SaveFileDialog
{
    Filter = "CSV Files|*.csv",
    FileName = "DataGridView_Export.csv"
};

if (dialog.ShowDialog() == DialogResult.OK)
    csvWorkbook.SaveAs(dialog.FileName);
WorkBook csvWorkbook = WorkBook.Create(ExcelFileFormat.CSV);
WorkSheet csvSheet = csvWorkbook.CreateWorkSheet("Data");

// Headers and data loops remain identical

using SaveFileDialog dialog = new SaveFileDialog
{
    Filter = "CSV Files|*.csv",
    FileName = "DataGridView_Export.csv"
};

if (dialog.ShowDialog() == DialogResult.OK)
    csvWorkbook.SaveAs(dialog.FileName);
$vbLabelText   $csharpLabel

CSV export is useful when the consuming system does not support XLSX or when file size is a concern for very large datasets.

How Do You Style the Header Row in the Exported File?

Exported data is more readable when the header row is visually distinct. IronXL provides cell styling options that let you apply bold text, background colour, and font size after writing the header values:

// Write headers and apply bold styling
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
    string cellAddress = worksheet.GetCellAddress(0, col);
    worksheet[cellAddress].Value = dataGridView1.Columns[col].HeaderText;
    worksheet[cellAddress].Style.Font.Bold = true;
    worksheet[cellAddress].Style.SetBackgroundColor("#4472C4");
    worksheet[cellAddress].Style.Font.FontColor = "#FFFFFF";
}
// Write headers and apply bold styling
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
    string cellAddress = worksheet.GetCellAddress(0, col);
    worksheet[cellAddress].Value = dataGridView1.Columns[col].HeaderText;
    worksheet[cellAddress].Style.Font.Bold = true;
    worksheet[cellAddress].Style.SetBackgroundColor("#4472C4");
    worksheet[cellAddress].Style.Font.FontColor = "#FFFFFF";
}
$vbLabelText   $csharpLabel

A styled header row makes the exported file immediately usable for reporting without requiring the recipient to apply formatting manually. For a full list of style properties, see the IronXL cell styling reference.

How Do You Get Started with a Free Trial?

IronXL provides a free trial that lets you test all features including the export workflows shown in this article. Install the package with either NuGet command shown above, generate a trial key on the IronXL licensing page, and set it in your application before the first library call:

IronXL.License.LicenseKey = "YOUR-LICENSE-KEY-HERE";
IronXL.License.LicenseKey = "YOUR-LICENSE-KEY-HERE";
$vbLabelText   $csharpLabel

The trial produces fully functional Excel files and does not restrict which features are available. For production deployment, licensing options cover single developers, teams, and royalty-free redistribution.

For related export scenarios, explore how IronXL handles exporting DataTables directly to Excel, reading Excel files into DataGridViews, and creating charts from exported data.

Choosing the Right Export Strategy for Your Use Case

The straightforward cell-by-cell export method shown throughout this tutorial suits most Windows Forms applications. If your DataGridView is bound to a DataTable, you can also pass the DataTable directly to IronXL's DataTable to worksheet converter, which handles the header row automatically. Choose the direct DataTable approach when your column layout is fixed and you want the shortest possible code path. Choose the manual cell-by-cell approach when you need conditional logic per cell -- for example, highlighting cells whose value falls outside an acceptable range, or mapping nullable types explicitly before writing.

For server-side scenarios such as ASP.NET Core controllers that generate downloadable Excel reports, the same IronXL API applies. The only difference is the save target: instead of SaveAs(filePath), call workbook.ToByteArray() and write the bytes to the HTTP response with a content type of application/vnd.openxmlformats-officedocument.spreadsheetml.sheet. Column headers transfer identically regardless of whether the output goes to a file or a stream.

Frequently Asked Questions

How can I export DataGridView data to Excel in C#?

Use the IronXL library to create an Excel workbook, iterate the DataGridView columns to write headers to row 0, then iterate the data rows and write cell values from row 1 onward. Save the workbook with SaveAs.

What is the benefit of using IronXL for exporting data?

IronXL provides a high-level API for Excel operations that requires no Microsoft Office installation. It handles headers, data types, and file formats without manual XML manipulation.

Is it possible to preserve column headers when exporting from DataGridView to Excel?

Yes. Read the HeaderText property of each DataGridView column and write it to row 0 of the IronXL worksheet before writing any data rows.

Do I need a trial to start using IronXL for exporting to Excel?

A free trial is available that gives access to all features. Install the NuGet package, generate a trial key, and set IronXL.License.LicenseKey before making any library calls.

Can IronXL handle large DataGridView datasets when exporting to Excel?

Yes. For very large grids, run the export on a background thread using Task.Run to keep the UI responsive while IronXL writes the data.

What are the steps to export DataGridView to Excel using IronXL?

Install IronXL, create a WorkBook and WorkSheet, loop through DataGridView columns to write headers at row 0, loop through rows to write data from row 1, then call SaveAs to write the file.

Why choose IronXL over other libraries for exporting to Excel?

IronXL requires no Office installation, runs on servers, and provides a clean WorkBook/WorkSheet API that maps naturally to how developers think about spreadsheets -- with strong documentation and active support.

Is there support available for troubleshooting issues with IronXL?

Yes. IronXL provides detailed API documentation, code examples, and direct engineering support for licensed customers.

Can I customize the Excel export process with IronXL?

Yes. After writing data you can apply bold fonts, background colours, borders, and number formats to any cell range using IronXL's Style API.

Does IronXL support exporting data to other formats besides Excel?

Yes. IronXL supports XLSX, XLS, CSV, and TSV formats. Switching formats requires only changing the ExcelFileFormat enum value passed to WorkBook.Create.

Jordi Bardia
Software Engineer
Jordi is most proficient in Python, C# and C++, when he isn’t leveraging his skills at Iron Software; he’s game programming. Sharing responsibilities for product testing, product development and research, Jordi adds immense value to continual product improvement. The varied experience keeps him challenged and engaged, and he ...
Read More

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me