From Excel to DataTable in C# with Open XML - c#

I'm using Visual Studio 2008 and I need create a DataTable from a Excel Sheet using the Open XML SDK 2.0. I need to create it with the DataTable columns with the first row of the sheet and complete it with the rest of values.
Does anyone have a example code or a link that can help me to do this?

I think this should do what you're asking. The other function is there just to deal with if you have shared strings, which I assume you do in your column headers. Not sure this is perfect, but I hope it helps.
static void Main(string[] args)
{
DataTable dt = new DataTable();
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(#"..\..\example.xlsx", false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
foreach (Cell cell in rows.ElementAt(0))
{
dt.Columns.Add(GetCellValue(spreadSheetDocument, cell));
}
foreach (Row row in rows) //this will also include your header row...
{
DataRow tempRow = dt.NewRow();
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i-1));
}
dt.Rows.Add(tempRow);
}
}
dt.Rows.RemoveAt(0); //...so i'm taking it out here.
}
public static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
string value = cell.CellValue.InnerXml;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
}
else
{
return value;
}
}

Hi The above code is working fine except one change
replace the below line of code
tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i-1));
with
tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
If you use (i-1) it will throw an exception:
specified argument was out of the range of valid values. parameter name index.

This solution works for spreadsheets without empty cells.
To handle empty cells, you will need to replace this line:
tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i-1));
with something like this:
Cell cell = row.Descendants<Cell>().ElementAt(i);
int index = CellReferenceToIndex(cell);
tempRow[index] = GetCellValue(spreadSheetDocument, cell);
And add this method:
private static int CellReferenceToIndex(Cell cell)
{
int index = -1;
string reference = cell.CellReference.ToString().ToUpper();
foreach (char ch in reference)
{
if (Char.IsLetter(ch))
{
int value = (int)ch - (int)'A';
index = (index + 1) * 26 + value;
}
else
return index;
}
return index;
}

This is my complete solution where empty cell is also taken into consideration.
public static class ExcelHelper
{
//To get the value of the cell, even it's empty. Unable to use loop by index
private static string GetCellValue(WorkbookPart wbPart, List<Cell> theCells, string cellColumnReference)
{
Cell theCell = null;
string value = "";
foreach (Cell cell in theCells)
{
if (cell.CellReference.Value.StartsWith(cellColumnReference))
{
theCell = cell;
break;
}
}
if (theCell != null)
{
value = theCell.InnerText;
// If the cell represents an integer number, you are done.
// For dates, this code returns the serialized value that represents the date. The code handles strings and
// Booleans individually. For shared strings, the code looks up the corresponding value in the shared string table. For Booleans, the code converts the value into the words TRUE or FALSE.
if (theCell.DataType != null)
{
switch (theCell.DataType.Value)
{
case CellValues.SharedString:
// For shared strings, look up the value in the shared strings table.
var stringTable = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
// If the shared string table is missing, something is wrong. Return the index that is in the cell. Otherwise, look up the correct text in the table.
if (stringTable != null)
{
value = stringTable.SharedStringTable.ElementAt(int.Parse(value)).InnerText;
}
break;
case CellValues.Boolean:
switch (value)
{
case "0":
value = "FALSE";
break;
default:
value = "TRUE";
break;
}
break;
}
}
}
return value;
}
private static string GetCellValue(WorkbookPart wbPart, List<Cell> theCells, int index)
{
return GetCellValue(wbPart, theCells, GetExcelColumnName(index));
}
private static string GetExcelColumnName(int columnNumber)
{
int dividend = columnNumber;
string columnName = String.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
return columnName;
}
//Only xlsx files
public static DataTable GetDataTableFromExcelFile(string filePath, string sheetName = "")
{
DataTable dt = new DataTable();
try
{
using (SpreadsheetDocument document = SpreadsheetDocument.Open(filePath, false))
{
WorkbookPart wbPart = document.WorkbookPart;
IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string sheetId = sheetName != "" ? sheets.Where(q => q.Name == sheetName).First().Id.Value : sheets.First().Id.Value;
WorksheetPart wsPart = (WorksheetPart)wbPart.GetPartById(sheetId);
SheetData sheetdata = wsPart.Worksheet.Elements<SheetData>().FirstOrDefault();
int totalHeaderCount = sheetdata.Descendants<Row>().ElementAt(0).Descendants<Cell>().Count();
//Get the header
for (int i = 1; i <= totalHeaderCount; i++)
{
dt.Columns.Add(GetCellValue(wbPart, sheetdata.Descendants<Row>().ElementAt(0).Elements<Cell>().ToList(), i));
}
foreach (Row r in sheetdata.Descendants<Row>())
{
if (r.RowIndex > 1)
{
DataRow tempRow = dt.NewRow();
//Always get from the header count, because the index of the row changes where empty cell is not counted
for (int i = 1; i <= totalHeaderCount; i++)
{
tempRow[i - 1] = GetCellValue(wbPart, r.Elements<Cell>().ToList(), i);
}
dt.Rows.Add(tempRow);
}
}
}
}
catch (Exception ex)
{
}
return dt;
}
}

First Add ExcelUtility.cs to your project :
ExcelUtility.cs
using System.Data;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
namespace Core_Excel.Utilities
{
static class ExcelUtility
{
public static DataTable Read(string path)
{
var dt = new DataTable();
using (var ssDoc = SpreadsheetDocument.Open(path, false))
{
var sheets = ssDoc.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
var relationshipId = sheets.First().Id.Value;
var worksheetPart = (WorksheetPart) ssDoc.WorkbookPart.GetPartById(relationshipId);
var workSheet = worksheetPart.Worksheet;
var sheetData = workSheet.GetFirstChild<SheetData>();
var rows = sheetData.Descendants<Row>().ToList();
foreach (var row in rows) //this will also include your header row...
{
var tempRow = dt.NewRow();
var colCount = row.Descendants<Cell>().Count();
foreach (var cell in row.Descendants<Cell>())
{
var index = GetIndex(cell.CellReference);
// Add Columns
for (var i = dt.Columns.Count; i <= index; i++)
dt.Columns.Add();
tempRow[index] = GetCellValue(ssDoc, cell);
}
dt.Rows.Add(tempRow);
}
}
return dt;
}
private static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
var stringTablePart = document.WorkbookPart.SharedStringTablePart;
var value = cell.CellValue.InnerXml;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
return stringTablePart.SharedStringTable.ChildElements[int.Parse(value)].InnerText;
return value;
}
public static int GetIndex(string name)
{
if (string.IsNullOrWhiteSpace(name))
return -1;
int index = 0;
foreach (var ch in name)
{
if (char.IsLetter(ch))
{
int value = ch - 'A' + 1;
index = value + index * 26;
}
else
break;
}
return index - 1;
}
}
}
Usage :
var path = "D:\\Documents\\test.xlsx";
var dt = ExcelUtility.Read(path);
then enjoy it!

Public Shared Function ExcelToDataTable(filename As String) As DataTable
Try
Dim dt As New DataTable()
Using doc As SpreadsheetDocument = SpreadsheetDocument.Open(filename, False)
Dim workbookPart As WorkbookPart = doc.WorkbookPart
Dim sheets As IEnumerable(Of Sheet) = doc.WorkbookPart.Workbook.GetFirstChild(Of Sheets)().Elements(Of Sheet)()
Dim relationshipId As String = sheets.First().Id.Value
Dim worksheetPart As WorksheetPart = DirectCast(doc.WorkbookPart.GetPartById(relationshipId), WorksheetPart)
Dim workSheet As Worksheet = worksheetPart.Worksheet
Dim sheetData As SheetData = workSheet.GetFirstChild(Of SheetData)()
Dim rows As IEnumerable(Of Row) = sheetData.Descendants(Of Row)()
For Each cell As Cell In rows.ElementAt(0)
dt.Columns.Add(GetCellValue(doc, cell))
Next
For Each row As Row In rows
'this will also include your header row...
Dim tempRow As DataRow = dt.NewRow()
For i As Integer = 0 To row.Descendants(Of Cell)().Count() - 1
tempRow(i) = GetCellValue(doc, row.Descendants(Of Cell)().ElementAt(i))
Next
dt.Rows.Add(tempRow)
Next
End Using
dt.Rows.RemoveAt(0)
Return dt
Catch ex As Exception
Throw ex
End Try
End Function
Public Shared Function GetCellValue(document As SpreadsheetDocument, cell As Cell) As String
Try
If IsNothing(cell.CellValue) Then
Return ""
End If
Dim value As String = cell.CellValue.InnerXml
If cell.DataType IsNot Nothing AndAlso cell.DataType.Value = CellValues.SharedString Then
Dim stringTablePart As SharedStringTablePart = document.WorkbookPart.SharedStringTablePart
Return stringTablePart.SharedStringTable.ChildElements(Int32.Parse(value)).InnerText
Else
Return value
End If
Catch ex As Exception
Return ""
End Try
End Function

I know it is a long time ago since this thread started. However, none of the solutions above did not really work for me. Empty cells issue and others.
I found a very good solution with 'MIT' license on GitHub:
https://github.com/ExcelDataReader/ExcelDataReader
This worked for me for both C# and VBnet applications.
Sample call from VBNET (the sample code for c# is on GitHub) :
Using stream As FileStream = New FileStream(DataPath & "\" & fName.Name, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
Using reader As IExcelDataReader = ExcelReaderFactory.CreateReader(stream)
ds = reader.AsDataSet(New ExcelDataSetConfiguration() With {
.UseColumnDataType = False,
.ConfigureDataTable = Function(tableReader) New ExcelDataTableConfiguration() With {
.UseHeaderRow = True
}
})
End Using
End Using
The result was a dataset with one table for each sheet in the workbook.
An I really like to compile the dll made in C# by myself rather then using a ready dll. So I can control what I am delivering to customers.

if rows value is null or empty get values wrong work.
all columns filled with data if it is working true. but maybe all rows not

Related

How to update Excel Cell based on validation using ClosedXML?

I have an input excel file which contains 6 columns and n number of rows. Out of which 3 columns are mandatory columns. If any of mandatory columns are empty then I need to update some custom text in the Remarks column. In the input excel column names are fixed but position is not fixed.
For example, in the below table I have updated Remarks column values of row no. 2 & 3 as Fail since CCode and ID column values are blank.
Name
ID
ComapanyName
CCode
Address
Remarks
Anto
12
ABC Corp Cmp
ABCCo
AvenueSt
Anuj
13
XYZ Corp Cmp
AvenueSt
Fail
Kathy
CTF Corp Cmp
CTFCo
AvenueSt
Fail
Close XML Logic:
var workbook = new XLWorkbook(IPPath);
var rows = workbook.Worksheet(1).RangeUsed().RowsUsed().Skip(1);
foreach (var row in rows)
{
-- update logic
}
First you need to read the excel data in DataTable, then you should be able to update the excel data by iterating each record.
private static System.Data.DataTable ReadExcelData(string filePath)
{
try
{
System.Data.DataTable dataTable = new System.Data.DataTable();
using (Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
using (XLWorkbook workBook = new XLWorkbook(stream))
{
var workSheet = workBook.Worksheet(1);
dataTable.TableName = workSheet.Name;
int lastRowIndex = workSheet.LastRowUsed().RowNumber();
int lastColumnIndex = workSheet.LastColumnUsed().ColumnNumber();
bool header = false;
foreach (IXLRow row in workSheet.Rows(1, lastRowIndex))
{
if (!header)
{
foreach (IXLCell cell in row.Cells(1, lastColumnIndex))
{
dataTable.Columns.Add(cell.GetFormattedString());
}
header = true;
}
dataTable.Rows.Add();
int i = 0;
foreach (IXLCell cell in row.Cells(1, lastColumnIndex))
{
dataTable.Rows[dataTable.Rows.Count - 1][i] = cell.GetFormattedString();
i++;
}
}
dataTable.Rows.RemoveAt(0);
}
}
return dataTable;
}
catch (Exception ex)
{
return null;
}
}
public static void UpdateData(string filePath, DataTable dataTable)
{
using (Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
using (XLWorkbook workBook = new XLWorkbook(stream))
{
var workSheet = workBook.Worksheet(1);
if (workSheet != null)
{
int ColumnIndex = 0;
int rowNumber = 1;
foreach (DataColumn column in dataTable.Columns)
{
if (column.ColumnName.Contains("Column Name"))
{
ColumnIndex = column.Ordinal + 1;
}
}
string colAddress = ColumnAddress(ColumnIndex);
foreach (DataRow row in dataTable.Rows)
{
rowNumber++;
workSheet.Cell(rowNumber, ColumnIndex).Value = "Update Value";
}
}
workBook.Save();
}
}
}
private static string ColumnAddress(int col)
{
if (col <= 26)
{
return Convert.ToString(Convert.ToChar(col + 64));
}
int div = col / 26;
int mod = col % 26;
if (mod == 0) { mod = 26; div--; }
return ColumnAddress(div) + ColumnAddress(mod);
}

How to add pivot table filters with Microsoft.Office.Interop.Excel in C#

I create a pivot table based on an Excel (.xlsx) file. The program adds fields to rows, values, and the filter. Using PivotFilters.Add2 causes 0x800a03ec error which terminates the program. How to properly use PivotFilters.Add2?
I tried filtering on different fields with different data types. Also, I tried using Type.Missing in a place of unused arguments. There seems to be plenty of information of this method for VB, but not so much for C#.
Items selected in the filter should be between the two dates on the last line.
var xlApp = new Microsoft.Office.Interop.Excel.Application();
xlApp.Visible = true;
var workBook = xlApp.Workbooks.Open(spreadsheetLocation);
var workSheet1 = (Microsoft.Office.Interop.Excel.Worksheet)workBook.Sheets["Data"];
var workSheet2 = (Microsoft.Office.Interop.Excel.Worksheet)workBook.Sheets.Add();
Microsoft.Office.Interop.Excel.Range last = workSheet1.Cells.SpecialCells(Microsoft.Office.Interop.Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
Microsoft.Office.Interop.Excel.Range range = workSheet1.get_Range("A1", last);
Microsoft.Office.Interop.Excel.PivotCaches pivotCaches = null;
Microsoft.Office.Interop.Excel.PivotCache pivotCache = null;
Microsoft.Office.Interop.Excel.PivotTable pivotTable = null;
Microsoft.Office.Interop.Excel.PivotFields pivotFields = null;
Microsoft.Office.Interop.Excel.PivotField filterField = null;
Microsoft.Office.Interop.Excel.PivotField accNumField = null;
Microsoft.Office.Interop.Excel.PivotField amountPaidField = null;
pivotCaches = workBook.PivotCaches();
pivotCache = pivotCaches.Create(XlPivotTableSourceType.xlDatabase, range);
pivotTable = pivotCache.CreatePivotTable(workSheet2.Cells[1,1]);
pivotFields = (Microsoft.Office.Interop.Excel.PivotFields)pivotTable.PivotFields();
amountPaidField = (Microsoft.Office.Interop.Excel.PivotField)pivotFields.Item("AmountPaid");
amountPaidField.Orientation = Microsoft.Office.Interop.Excel.XlPivotFieldOrientation.xlDataField;
amountPaidField.NumberFormat = "$#,###,###.00";
accNumField = (Microsoft.Office.Interop.Excel.PivotField)pivotFields.Item("AccNumber");
accNumField.Orientation = Microsoft.Office.Interop.Excel.XlPivotFieldOrientation.xlRowField;
filterField = (Microsoft.Office.Interop.Excel.PivotField)pivotFields.Item("AccDate");
filterField.Orientation = Microsoft.Office.Interop.Excel.XlPivotFieldOrientation.xlPageField;
filterField.EnableMultiplePageItems = true;
filterField.PivotFilters.Add2(XlPivotFilterType.xlDateBetween, Type.Missing, DateTime.Now.AddDays(-30), DateTime.Now.AddDays(-20));
#First it deletes two rows and then it creates a pivot table#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Office.Interop.Excel;
namespace ConsoleApplication4
{
class Program
{
public static string Column(int column)
{
column--;
if (column >= 0 && column < 26)
return ((char)('A' + column)).ToString();
else if (column > 25)
return Column(column / 26) + Column(column % 26 + 1);
else
throw new Exception("Invalid Column #" + (column + 1).ToString());
}
static void Main(string[] args)
{
try
{
string path = #"C:\Users\UX155512\Documents\Book1fd.xlsx";
var excelFile = new Application();
Workbook workBook = excelFile.Workbooks.Open(path);
Worksheet workSheet = workBook.Worksheets[1];
Worksheet pivotSheet = workBook.Worksheets.Add(
System.Reflection.Missing.Value,
workBook.Worksheets[workBook.Worksheets.Count],
1,
System.Reflection.Missing.Value);
Range last = workSheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell, Type.Missing);
int lastRow = last.Row;
int lastCol = last.Column;
string lastColVal = Column(lastCol);
string lastFilledCol = lastColVal + lastRow.ToString();
Console.WriteLine(lastFilledCol);
Console.WriteLine(lastColVal);
Console.WriteLine(lastRow);
Console.WriteLine(lastCol);
for (int i = 1; i <= lastRow; i++)
{
if (workSheet.Cells[1][i].Value == ("HDR"))
{
workSheet.Rows[i].Delete();
Console.WriteLine("The Row Containing HDR has been deleted");
}
if (workSheet.Cells[1][i].Value == ("TRL"))
{
workSheet.Rows[i].Delete();
Console.WriteLine("The Row Containing TLR has been deleted");
}
}
Range lastRange = workSheet.Range["A1", lastFilledCol];
pivotSheet.Name = "Pivot Table";
Range range = pivotSheet.Cells[1, 1];
PivotCache pivotCache = (PivotCache)workBook.PivotCaches().Add(XlPivotTableSourceType.xlDatabase, lastRange);
PivotTable pivotTable = (PivotTable)pivotSheet.PivotTables().Add(PivotCache: pivotCache, TableDestination: range);
PivotField pivotField = (PivotField)pivotTable.PivotFields("Plan Number");
pivotField.Orientation = XlPivotFieldOrientation.xlRowField;
PivotField pivotField2 = (PivotField)pivotTable.PivotFields("Source");
pivotField2.Orientation = XlPivotFieldOrientation.xlColumnField;
PivotField pivotField3 = (PivotField)pivotTable.PivotFields("Total");
pivotField3.Orientation = XlPivotFieldOrientation.xlDataField;
pivotField3.Function = XlConsolidationFunction.xlSum;
workBook.SaveAs(#"C:\Users\UX155512\Documents\Excel Dump\Trial9.xlsx");
workBook.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Read();
}
}
}
As mentioned above by Asger, the filter can't be added to a page field. Instead, the pivot item's visibility property has to be set.
var pivotItems = filterField.PivotItems();
DateTime date = Convert.ToDateTime(item.Name);
foreach (var item in pivotItems)
{
item.Visible = false;
if (date < DateTime.Now.AddDays(-30) || date > DateTime.Now.AddDays(-20))
{
item.Visible = true;
}
}
The best way to find the answer to pretty much ANY interop question is to record a macro then examine the source.

Optimize reading of an excel file into a DataTable object

I use the below code to read data from an excel file into a DataTable object for further use. Since it processes from 100k to 500k entries, the reading can get a bit slow. Is there something I could change in my code in order to optimize the process ? Code is below.
public static DataTable ReadAsDataTable(string filePath)
{
DataTable dataTable = new DataTable();
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(filePath, false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
foreach (Cell cell in rows.ElementAt(0))
{
dataTable.Columns.Add(GetCellValue(spreadSheetDocument, cell));
}
foreach (Row row in rows)
{
DataRow dataRow = dataTable.NewRow();
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
dataRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
}
dataTable.Rows.Add(dataRow);
}
}
dataTable.Rows.RemoveAt(0);
return dataTable;
}
private static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
string value = cell.CellValue.InnerXml;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
}
else
{
return value;
}
}
I'm not sure what's the compiler behavior on this or the performance characteristics of that API, but would it help if you call row.Descendants<Cell>() only once? It seems like something the compiler could optimize, but there may be side effects involved, so it doesn't do anything.
foreach (Row row in rows)
{
var cells = row.Descendants<Cell>().ToArray();
DataRow dataRow = dataTable.NewRow();
for (int i = 0; i < cells.Length; i++)
{
dataRow[i] = GetCellValue(spreadSheetDocument, cells[i]);
}
dataTable.Rows.Add(dataRow);
}

How to convert each column in a new table in DocumentFormat.OpenXML

I have an issue at converting from Excel file to XML file. When I convert the info in table tags, it converts in <table1></table1> <table1></table1> <table1></table1>but I need to convert each column in <table1></table1><table2></table2>...
The code is the following.
private DataTable ReadExcelFile(string filename)
{
// Initialize an instance of DataTable
DataTable dt = new DataTable();
try
{
// Use SpreadSheetDocument class of Open XML SDK to open excel file
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(filename, false))
{
// Get Workbook Part of Spread Sheet Document
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
// Get all sheets in spread sheet document
// IEnumerable Interface
// Exposes an enumerator, which supports a simple iteration over a non-generic collection.
IEnumerable<Sheet> sheetcollection = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
// Get relationship Id
string relationshipId = sheetcollection.First().Id.Value;
// Get sheet1 Part of Spread Sheet Document
WorksheetPart worksheetPart = (WorksheetPart)spreadsheetDocument.WorkbookPart.GetPartById(relationshipId);
// Get Data in Excel file
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
IEnumerable<Row> rowcollection = sheetData.Descendants<Row>();
if (rowcollection.Count() == 0)
{
return dt;
}
// Add columns
foreach (Cell cell in rowcollection.ElementAt(0))
{
dt.Columns.Add(GetValueOfCell(spreadsheetDocument, cell));
}
// Add rows into DataTable
foreach (Row row in rowcollection)
{
DataRow temprow = dt.NewRow();
int columnIndex = 0;
foreach (Cell cell in row.Descendants<Cell>())
{
// Get Cell Column Index
int cellColumnIndex = GetColumnIndex(GetColumnName(cell.CellReference));
if (columnIndex < cellColumnIndex)
{
do
{
temprow[columnIndex] = string.Empty;
columnIndex++;
}
while (columnIndex < cellColumnIndex);
}
temprow[columnIndex] = GetValueOfCell(spreadsheetDocument, cell);
columnIndex++;
}
// Add the row to DataTable
// the rows include header row
dt.Rows.Add(temprow);
}
}
// Here remove header row
dt.Rows.RemoveAt(0);
return dt;
}
catch (IOException ex)
{
throw new IOException(ex.Message);
}
}
private static string GetValueOfCell(SpreadsheetDocument spreadsheetdocument, Cell cell)
{
// Get value in Cell
SharedStringTablePart sharedString = spreadsheetdocument.WorkbookPart.SharedStringTablePart;
if (cell.CellValue == null)
{
return string.Empty;
}
string cellValue = cell.CellValue.InnerText;
// The condition that the Cell DataType is SharedString
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
return sharedString.SharedStringTable.ChildElements[int.Parse(cellValue)].InnerText;
}
else
{
return cellValue;
}
}
private string GetColumnName(string cellReference)
{
// Create a regular expression to match the column name of cell
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellReference);
return match.Value;
}
private int GetColumnIndex(string columnName)
{
int columnIndex = 0;
int factor = 1;
// From right to left
for (int position = columnName.Length - 1; position >= 0; position--)
{
// For letters
if (Char.IsLetter(columnName[position]))
{
columnIndex += factor * ((columnName[position] - 'A') + 1) - 1;
factor *= 26;
}
}
return columnIndex;
}
public string GetXML(string filename)
{
using (DataSet ds = new DataSet())
{
ds.Tables.Add(this.ReadExcelFile(filename));
return ds.GetXml();
}
}
May somebody help me out with this issue?
Or any idea how to create a custom tags?
Thanks in advance.

c# (WinForms-App) export DataSet to Excel

I need a solution to export a dataset to an excel file without any asp code (HttpResonpsne...) but i did not find a good example to do this...
Best thanks in advance
I've created a class that exports a DataGridView or DataTable to an Excel file. You can probably change it a bit to make it use your DataSet instead (iterating through the DataTables in it). It also does some basic formatting which you could also extend.
To use it, simply call ExcelExport, and specify a filename and whether to open the file automatically or not after exporting. I also could have made them extension methods, but I didn't. Feel free to.
Note that Excel files can be saved as a glorified XML document and this makes use of that.
EDIT: This used to use a vanilla StreamWriter, but as pointed out, things would not be escaped correctly in many cases. Now it uses a XmlWriter, which will do the escaping for you.
The ExcelWriter class wraps an XmlWriter. I haven't bothered, but you might want to do a bit more error checking to make sure you can't write cell data before starting a row, and such. The code is below.
public class ExcelWriter : IDisposable
{
private XmlWriter _writer;
public enum CellStyle { General, Number, Currency, DateTime, ShortDate };
public void WriteStartDocument()
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteProcessingInstruction("mso-application", "progid=\"Excel.Sheet\"");
_writer.WriteStartElement("ss", "Workbook", "urn:schemas-microsoft-com:office:spreadsheet");
WriteExcelStyles();
}
public void WriteEndDocument()
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteEndElement();
}
private void WriteExcelStyleElement(CellStyle style)
{
_writer.WriteStartElement("Style", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteAttributeString("ID", "urn:schemas-microsoft-com:office:spreadsheet", style.ToString());
_writer.WriteEndElement();
}
private void WriteExcelStyleElement(CellStyle style, string NumberFormat)
{
_writer.WriteStartElement("Style", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteAttributeString("ID", "urn:schemas-microsoft-com:office:spreadsheet", style.ToString());
_writer.WriteStartElement("NumberFormat", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteAttributeString("Format", "urn:schemas-microsoft-com:office:spreadsheet", NumberFormat);
_writer.WriteEndElement();
_writer.WriteEndElement();
}
private void WriteExcelStyles()
{
_writer.WriteStartElement("Styles", "urn:schemas-microsoft-com:office:spreadsheet");
WriteExcelStyleElement(CellStyle.General);
WriteExcelStyleElement(CellStyle.Number, "General Number");
WriteExcelStyleElement(CellStyle.DateTime, "General Date");
WriteExcelStyleElement(CellStyle.Currency, "Currency");
WriteExcelStyleElement(CellStyle.ShortDate, "Short Date");
_writer.WriteEndElement();
}
public void WriteStartWorksheet(string name)
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteStartElement("Worksheet", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteAttributeString("Name", "urn:schemas-microsoft-com:office:spreadsheet", name);
_writer.WriteStartElement("Table", "urn:schemas-microsoft-com:office:spreadsheet");
}
public void WriteEndWorksheet()
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteEndElement();
_writer.WriteEndElement();
}
public ExcelWriter(string outputFileName)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
_writer = XmlWriter.Create(outputFileName, settings);
}
public void Close()
{
if (_writer == null) throw new InvalidOperationException("Already closed.");
_writer.Close();
_writer = null;
}
public void WriteExcelColumnDefinition(int columnWidth)
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteStartElement("Column", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteStartAttribute("Width", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteValue(columnWidth);
_writer.WriteEndAttribute();
_writer.WriteEndElement();
}
public void WriteExcelUnstyledCell(string value)
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteStartElement("Cell", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteStartElement("Data", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "String");
_writer.WriteValue(value);
_writer.WriteEndElement();
_writer.WriteEndElement();
}
public void WriteStartRow()
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteStartElement("Row", "urn:schemas-microsoft-com:office:spreadsheet");
}
public void WriteEndRow()
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteEndElement();
}
public void WriteExcelStyledCell(object value, CellStyle style)
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
_writer.WriteStartElement("Cell", "urn:schemas-microsoft-com:office:spreadsheet");
_writer.WriteAttributeString("StyleID", "urn:schemas-microsoft-com:office:spreadsheet", style.ToString());
_writer.WriteStartElement("Data", "urn:schemas-microsoft-com:office:spreadsheet");
switch (style)
{
case CellStyle.General:
_writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "String");
break;
case CellStyle.Number:
case CellStyle.Currency:
_writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "Number");
break;
case CellStyle.ShortDate:
case CellStyle.DateTime:
_writer.WriteAttributeString("Type", "urn:schemas-microsoft-com:office:spreadsheet", "DateTime");
break;
}
_writer.WriteValue(value);
// tag += String.Format("{1}\"><ss:Data ss:Type=\"DateTime\">{0:yyyy\\-MM\\-dd\\THH\\:mm\\:ss\\.fff}</ss:Data>", value,
_writer.WriteEndElement();
_writer.WriteEndElement();
}
public void WriteExcelAutoStyledCell(object value)
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
//write the <ss:Cell> and <ss:Data> tags for something
if (value is Int16 || value is Int32 || value is Int64 || value is SByte ||
value is UInt16 || value is UInt32 || value is UInt64 || value is Byte)
{
WriteExcelStyledCell(value, CellStyle.Number);
}
else if (value is Single || value is Double || value is Decimal) //we'll assume it's a currency
{
WriteExcelStyledCell(value, CellStyle.Currency);
}
else if (value is DateTime)
{
//check if there's no time information and use the appropriate style
WriteExcelStyledCell(value, ((DateTime)value).TimeOfDay.CompareTo(new TimeSpan(0, 0, 0, 0, 0)) == 0 ? CellStyle.ShortDate : CellStyle.DateTime);
}
else
{
WriteExcelStyledCell(value, CellStyle.General);
}
}
#region IDisposable Members
public void Dispose()
{
if (_writer == null)
return;
_writer.Close();
_writer = null;
}
#endregion
}
Then you can export your DataTable using the following:
public static void ExcelExport(DataTable data, String fileName, bool openAfter)
{
//export a DataTable to Excel
DialogResult retry = DialogResult.Retry;
while (retry == DialogResult.Retry)
{
try
{
using (ExcelWriter writer = new ExcelWriter(fileName))
{
writer.WriteStartDocument();
// Write the worksheet contents
writer.WriteStartWorksheet("Sheet1");
//Write header row
writer.WriteStartRow();
foreach (DataColumn col in data.Columns)
writer.WriteExcelUnstyledCell(col.Caption);
writer.WriteEndRow();
//write data
foreach (DataRow row in data.Rows)
{
writer.WriteStartRow();
foreach (object o in row.ItemArray)
{
writer.WriteExcelAutoStyledCell(o);
}
writer.WriteEndRow();
}
// Close up the document
writer.WriteEndWorksheet();
writer.WriteEndDocument();
writer.Close();
if (openAfter)
OpenFile(fileName);
retry = DialogResult.Cancel;
}
}
catch (Exception myException)
{
retry = MessageBox.Show(myException.Message, "Excel Export", MessageBoxButtons.RetryCancel, MessageBoxIcon.Asterisk);
}
}
}
The following site demonstrates how to export a DataSet (or DataTable or List<>) into a "genuine" Excel 2007 .xlsx file.
It uses the OpenXML libraries, so you don't need to have Excel installed on your server.
C# ExportToExcel library
All of the source code is provided, free of charge, along with instructions using it with ASP.NET, ASP.NET Core 2+ or regular C#.
It's very easy to add to your own applications, you just need to call one function, passing in an Excel filename, and your data source:
DataSet ds = CreateSampleData();
string excelFilename = "C:\\Sample.xlsx";
CreateExcelFile.CreateExcelDocument(ds, excelFilename);
Hope this helps.
Creating excel files in .NET applications is quite common and similar questions have been asked several times before. For instance here and here. The last question asks about reading excel files, but most suggested solutions should work both ways.
This was a post with a really helpful top answer, but I found it was lacking because there was no simple way to import the XML file back into a datatable. I ended up having to write my own, and thought I would share it here in case anyone else was in the same boat (google was exceptionally unhelpful in that regard):
public static DataTable ImportExcelXML(string Filename)
{
//create a new dataset to load in the XML file
DataSet DS = new DataSet();
//Read the XML file into the dataset
DS.ReadXml(Filename);
//Create a new datatable to store the raw Data
DataTable Raw = new DataTable();
//assign the raw data from the file to the datatable
Raw = DS.Tables["Data"];
//count the number of columns in the XML file
int ColumnNumber = Raw.Columns.Count;
//create a datatable to store formatted Import Data
DataTable ImportData = new DataTable();
//create a string list to store the cell data of each row
List<string> RowData = new List<string>();
//loop through each row in the raw data table
for (int Counter = 0; Counter < Raw.Rows.Count; Counter++)
{
//if the data in the row is a colum header
if (Counter < ColumnNumber)
{
//add the column name to our formatted datatable
ImportData.Columns.Add(Raw.Rows[Counter].ItemArray.GetValue(1).ToString());
}
else
{
//if the row # (1 row = 1 cell from the excel file) from the raw datatable is divisable evenly by the number of columns in the formated import datatable AND this is not the 1st row of the raw table data after the headers
if ((Counter % ColumnNumber == 0) && (Counter != ColumnNumber))
{
//add the row we just built to the formatted import datatable
ImportData.Rows.Add(GenerateRow(ImportData, RowData));
//clear rowdata list in preperation for the next row
RowData.Clear();
}
//add the current cell data value from the raw datatable to the string list of cell values for the next row to be added to the formatted input datatable
RowData.Add(Raw.Rows[Counter].ItemArray.GetValue(1).ToString());
}
}
//add the final row
ImportData.Rows.Add(GenerateRow(ImportData, RowData));
return ImportData;
}
public static DataRow GenerateRow(DataTable ImportData, List<string> RowData)
{
//create a counter to keep track of the column position during row composition
int ColumnPosition = 0;
//make a new datarow based on the schema of the formated import datatable
DataRow NewRow = ImportData.NewRow();
//for each string cell value collected for the RowData list for this row
foreach (string CellData in RowData)
{
//add the cell value to the new datarow
NewRow[ImportData.Columns[ColumnPosition].ColumnName] = CellData;
//incriment column position in the new row
ColumnPosition++;
}
//return the generated row
return NewRow;
}
the code have problem with null values.
public void WriteExcelAutoStyledCell(object value)
{
//solve null values
if (value is DBNull) return;
I would have added this to the comments but I am new to stack so I am unable to comment. Using lc.'s solution I added another function that tests the strings characters for invalid XML characters. When I was exporting to excel on occasion there were characters that were found that were causing the export to fail.
You will need to modify one of the functions that is in lc's code.
public void WriteExcelAutoStyledCell(object value)
{
if (_writer == null) throw new InvalidOperationException("Cannot write after closing.");
string newValue = string.Empty;
try
{
//write the <ss:Cell> and <ss:Data> tags for something
if (value is Int16 || value is Int32 || value is Int64 || value is SByte ||
value is UInt16 || value is UInt32 || value is UInt64 || value is Byte)
{
WriteExcelStyledCell(value, CellStyle.Number);
}
else if (value is Single || value is Double || value is Decimal) //we'll assume it's a currency
{
WriteExcelStyledCell(value, CellStyle.Currency);
}
else if (value is DateTime)
{
//check if there's no time information and use the appropriate style
WriteExcelStyledCell(value, ((DateTime)value).TimeOfDay.CompareTo(new TimeSpan(0, 0, 0, 0, 0)) == 0 ? CellStyle.ShortDate : CellStyle.DateTime);
}
else
{
newValue = CheckXmlCompatibleValues(value.ToString()).ToString();
WriteExcelStyledCell(newValue, CellStyle.General);
}
}
catch (Exception thisException)
{
throw new InvalidOperationException(thisException.Message.ToString());
}
}
And add this function to 'ExcelWriter' class
public string CheckXmlCompatibleValues(string value)
{
string newValue = string.Empty;
bool found = false;
foreach (char c in value)
{
if (XmlConvert.IsXmlChar(c))
newValue += c.ToString();
else
found = true;
}
return newValue.ToString();
}
lc. Thanks for the code!
using XL = Microsoft.Office.Interop.Excel;
using System.Reflection;
public static void Datasource(DataTable dt)
{
XL.Application oXL;
XL._Workbook oWB;
XL._Worksheet oSheet;
XL.Range oRng;
try
{
oXL = new XL.Application();
Application.DoEvents();
oXL.Visible = false;
//Get a new workbook.
oWB = (XL._Workbook)(oXL.Workbooks.Add(Missing.Value));
oSheet = (XL._Worksheet)oWB.ActiveSheet;
//System.Data.DataTable dtGridData=ds.Tables[0];
int iRow = 2;
if (dt.Rows.Count > 0)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
oSheet.Cells[1, j + 1] = dt.Columns[j].ColumnName;
}
// For each row, print the values of each column.
for (int rowNo = 0; rowNo < dt.Rows.Count; rowNo++)
{
for (int colNo = 0; colNo < dt.Columns.Count; colNo++)
{
oSheet.Cells[iRow, colNo + 1] = dt.Rows[rowNo][colNo].ToString();
}
iRow++;
}
iRow++;
}
oRng = oSheet.get_Range("A1", "IV1");
oRng.EntireColumn.AutoFit();
oXL.Visible = true;
}
catch (Exception theException)
{
throw theException;
}
finally
{
oXL = null;
oWB = null;
oSheet = null;
oRng = null;
}
}
Import from Excel to datatable:
DataTable dtTable = new DataTable();
DataColumn col = new DataColumn("Rfid");
dtTable.Columns.Add(col);
DataRow drRow;
Microsoft.Office.Interop.Excel.Application ExcelObj =
new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook theWorkbook =
ExcelObj.Workbooks.Open(txt_FilePath.Text, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
Microsoft.Office.Interop.Excel.Sheets sheets = theWorkbook.Worksheets;
try
{
for (int sht = 1; sht <= sheets.Count; sht++)
{
Microsoft.Office.Interop.Excel.Worksheet worksheet =
(Microsoft.Office.Interop.Excel.Worksheet)sheets.get_Item(sht);
for (int i = 2; i <= worksheet.UsedRange.Rows.Count; i++)
{
Microsoft.Office.Interop.Excel.Range range = worksheet.get_Range("A" + i.ToString(), "B" + i.ToString());
System.Array myvalues = (System.Array)range.Cells.Value2;
String name = Convert.ToString(myvalues.GetValue(1, 1));
if (string.IsNullOrEmpty(name) == false)
{
drRow = dtTable.NewRow();
drRow["Rfid"] = name;
dtTable.Rows.Add(drRow);
}
}
Marshal.ReleaseComObject(worksheet);
worksheet = null;
}
return dtTable;
}
catch
{
throw;
}
finally
{
// Marshal.ReleaseComObject(worksheet);
Marshal.ReleaseComObject(sheets);
Marshal.ReleaseComObject(theWorkbook);
Marshal.ReleaseComObject(ExcelObj);
//worksheet = null;
sheets = null;
theWorkbook = null;
ExcelObj = null;
}
Microsoft has a built in solution for Importing/Exporting Excel files. It's not the most straightforward library but it generally works better than the others listed above.
The library required to do this is included in Office and can be found under the list of Framework assemblies at Microsoft.Office.Interop.Excel.
Here is some example code:
using Excel = Microsoft.Office.Interop.Excel;
Excel.Application app = new Excel.Application();
//Open existing workbook
//Excel.Workbook workbook = xlApp.Workbooks.Open(fileName);
//Create new workbook
Excel.Workbook workbook = app.Workbooks.Add();
Excel.Worksheet worksheet = workbook.ActiveSheet;
worksheet.Cells[1,1] = "Hello world!"; // Indexes start at 1, because Excel
workbook.SaveAs("C:\\MyWorkbook.xlsx");
workbook.Close();
app.Quit();

Categories

Resources