I'm creating a new open xml document. When I write unique data/rows to the document I can open it a variety of programs. When I write a non-unique row and attempt to open the document in Apache OpenOffice I get an error General Error. General input/output error. Obviously this isn't very descriptive so I'm assuming I'm creating my document wrong but I'm not sure what is missing/wrong
Things I've tried:
The solution listed in the OpenOffice Documentation
Using the OpenXmlValidator
This doesn't return any errors
Opening in different software: Microsoft Office Excel Viewer and LibreOffice Calc.
The file opens in these but the machines running this code don't have this software installed
The weird fix
Rename the a.xlsx => a.zip
Extract the contents from the zip file
Zip up all the contents (using winrar and windows compressed zipped folder) named b.zip
Rename the b.zip to b.xlsx
The file now opens in OpenOffice without any error.
Doing a diff on the unzipped files shows no differences, doing a diff on a.xlsx and b.xlsx there are differences but nothing that makes sense to me
The Code:
static void Main(string[] args) {
var thing = new MyClass();
thing.GenerateDoc();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
public class MyClass {
public MyClass() { }
public void GenerateDoc() {
var xmlFileString = "Temp.xlsx";
var sheetName = "sheetName";
var OpenXMLAlwaysPrintHeader = true;
try {
bool fileExists = System.IO.File.Exists(xmlFileString);
if (!fileExists) {
// check for a blank file template and copy that if it exists
CreateSpreadsheetWorkbook(xmlFileString, sheetName);
}
fileExists = System.IO.File.Exists(xmlFileString);
if (fileExists) {
UInt32 RowIndex;
using (var doc = SpreadsheetDocument.Open(xmlFileString, true)) {
// Check to see if the sheet we are adding data to exists
var workbookPart = doc.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.FirstOrDefault();
try {
worksheetPart = GetWorksheetPart(workbookPart, sheetName);
}
catch (Exception) { }
var sheet = worksheetPart.Worksheet ?? new Worksheet();
var sheetData = sheet.Elements<SheetData>().First();
var t = sheetData.Elements<Row>();
Row eHeader = null;
if (t.Count() > 0) {
eHeader = t?.First(); // This should be the first row ( the header or key of each item in the dict)
}
RowIndex = (uint)t.Count() + 1;
// Create the table of all strings if it doesnt exist
SharedStringTablePart shareStringPart;
if (doc.WorkbookPart.GetPartsOfType<SharedStringTablePart>().Count() > 0)
shareStringPart = doc.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
else
shareStringPart = doc.WorkbookPart.AddNewPart<SharedStringTablePart>();
// Create a row for the header and the values (referring to the keys and the values in the dict)
Row header = new Row() { RowIndex = RowIndex++ };
Row data = new Row() { RowIndex = RowIndex };
// If we are not re-printing the header than the data row needs to shift up by 1
if (!OpenXMLAlwaysPrintHeader && t.Count() > 0)
data.RowIndex = --data.RowIndex;
var ColIndex = 1;
// Create the DefinedNames part which is a dictonary of "string" to a range of cells
// todo fix the next line
// This deletes all other pre-defined names since I can't figure out how to update a defined name yet
var dfns = new DefinedNames();
workbookPart.Workbook.DefinedNames = dfns;
/*
data.RowIndex = data.RowIndex - 1;
// Row 1
InsertObjectAt("Test1", data.RowIndex, ColIndex++, worksheetPart, shareStringPart);
InsertObjectAt("TestB", data.RowIndex++, ColIndex, worksheetPart, shareStringPart);
// Row 2
InsertObjectAt("Test2", data.RowIndex, --ColIndex, worksheetPart, shareStringPart);
InsertObjectAt("TestB", data.RowIndex++, ++ColIndex, worksheetPart, shareStringPart);
// Row 3
InsertObjectAt("Test1", data.RowIndex, --ColIndex, worksheetPart, shareStringPart);
InsertObjectAt("TestB1", data.RowIndex++, ++ColIndex, worksheetPart, shareStringPart);
// Row 4
InsertObjectAt("Test2", data.RowIndex, --ColIndex, worksheetPart, shareStringPart);
InsertObjectAt("TestB1", data.RowIndex++, ++ColIndex, worksheetPart, shareStringPart);
// */
sheet.SheetDimension = new SheetDimension() { Reference = "A1:B4" };
for (int i = 0; i < 2; i++) {
if (!OpenXMLAlwaysPrintHeader && t.Count() > 0) // Look up which column we want to insert our value into
{
var indexOfItem = InsertSharedStringItem("Key", shareStringPart);
var cells = eHeader.Elements<Cell>().Where(x => x.CellValue.InnerText == indexOfItem.ToString());
if (cells.Count() < 1) continue;
var cell = cells.First();
ColIndex = GetColumnIndex(cell?.CellReference).Value;
} // Otherwise we are always inserting a header so don't bother looking up where things should go
else {
//Insert for the header
InsertObjectAt(ColIndex%2 == 0 ? "TestB" : "Test1", header.RowIndex, ColIndex, worksheetPart, shareStringPart);
}
// Insert for the data
if (RowIndex == 2) {
InsertObjectAt((i % 2 == 0 ? "Test2" : "TestC"), data.RowIndex, ColIndex++, worksheetPart, shareStringPart);
}else {
InsertObjectAt((i % 2 == 0 ? "Test2" : "TestD"), data.RowIndex, ColIndex++, worksheetPart, shareStringPart);
}
/*
if (!OpenXMLAlwaysPrintHeader) // If we are not always printing a header we can create a named range for the column
CreateRange(workbookPart, "key", sheetName, data.RowIndex, ColIndex - 1);
// */
}
}
}
var validator = new OpenXmlValidator();
int count = 0;
var stringbuilder = new StringBuilder();
foreach (ValidationErrorInfo error in validator.Validate(SpreadsheetDocument.Open(xmlFileString, true))) {
stringbuilder.Append("\r\n");
count++;
stringbuilder.Append(("Error Count : " + count) + "\r\n");
stringbuilder.Append(("Description : " + error.Description) + "\r\n");
stringbuilder.Append(("Path: " + error.Path.XPath) + "\r\n");
stringbuilder.Append(("Part: " + error.Part.Uri) + "\r\n");
}
}
catch (Exception e) {
e = e;
}
}
private 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;
}
public void CreateRange(WorkbookPart wbPart, string Name, string SheetName, uint RowIndex, int ColIndex) {
var definedNames = wbPart.Workbook.DefinedNames;
var myLocation = GetExcelColumnName(ColIndex) + RowIndex.ToString();
var Col = GetExcelColumnName(ColIndex);
var Text = string.Format("{0}!${1}${2}:${3}${4}", SheetName, Col, 2, Col, RowIndex);
var colRange = new DefinedName { Name = Name, Text = Text };
wbPart.Workbook.DefinedNames?.Append(colRange);
}
private static int? GetColumnIndex(string cellReference) {
if (string.IsNullOrEmpty(cellReference)) {
return null;
}
//remove digits
string columnReference = Regex.Replace(cellReference.ToUpper(), #"[\d]", string.Empty);
int columnNumber = -1;
int mulitplier = 1;
//working from the end of the letters take the ASCII code less 64 (so A = 1, B =2...etc)
//then multiply that number by our multiplier (which starts at 1)
//multiply our multiplier by 26 as there are 26 letters
foreach (char c in columnReference.ToCharArray().Reverse()) {
columnNumber += mulitplier * ((int)c - 64);
mulitplier = mulitplier * 26;
}
//the result is zero based so return columnnumber + 1 for a 1 based answer
//this will match Excel's COLUMN function
return columnNumber + 1;
}
private void InsertObjectAt(object item, uint RowIndex, int ColIndex, WorksheetPart worksheetPart, SharedStringTablePart sharedStringTablePart) {
if (item == null) return;
if (item is ICollection)
item = ICollectionToString(item as ICollection);
// Create the header cell
int index = InsertSharedStringItem(item.ToString(), sharedStringTablePart);
Cell c = InsertCellInWorksheet(GetExcelColumnName(ColIndex), RowIndex, worksheetPart);
c.CellValue = new CellValue(index.ToString());
c.DataType = new EnumValue<CellValues>(CellValues.SharedString);
}
private static int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart) {
// If the part does not contain a SharedStringTable, create one.
if (shareStringPart.SharedStringTable == null) {
shareStringPart.SharedStringTable = new SharedStringTable();
}
int i = 0;
// Iterate through all the items in the SharedStringTable. If the text already exists, return its index.
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>()) {
if (item.InnerText == text) {
return i;
}
i++;
}
// The text does not exist in the part. Create the SharedStringItem and return its index.
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(text)));
shareStringPart.SharedStringTable.Save();
return i;
}
public static WorksheetPart GetWorksheetPart(WorkbookPart workbookPart, string sheetName) {
Sheet sheet = workbookPart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == sheetName);
if (sheet == default(Sheet)) {
CreateSheet(workbookPart, sheetName);
}
return workbookPart.GetPartById(sheet.Id) as WorksheetPart;
}
public static void CreateSheet(WorkbookPart workbookPart, string sheetName) {
var sheets = workbookPart.Workbook.Descendants<Sheets>().FirstOrDefault();
if (sheets == default(Sheets))
sheets = workbookPart.Workbook.AppendChild(new Sheets());
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
var sheetdata = new SheetData();
var worksheet = new Worksheet(sheetdata);
worksheetPart.Worksheet = worksheet;
var id = (UInt32)workbookPart.Workbook.Descendants<Sheet>().Count() + 1;
var sheet = new Sheet() { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = id, Name = sheetName };
sheets.AppendChild(sheet);
workbookPart.Workbook.Save();
}
public static void CreateSpreadsheetWorkbook(string filepath, string sheetName) {
// Create a spreadsheet document by supplying the filepath.
// By default, AutoSave = true, Editable = true, and Type = xlsx.
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.
Create(filepath, SpreadsheetDocumentType.Workbook);
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.
AppendChild<Sheets>(new Sheets());
// Append a new worksheet and associate it with the workbook.
var id = (UInt32)workbookpart.Workbook.Descendants<Sheet>().Count() + 1;
Sheet sheet = new Sheet() {
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = id,
Name = sheetName
};
sheets.Append(sheet);
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
}
private string ICollectionToString(ICollection item) {
try {
var result = string.Empty;
if (item is IDictionary) {
foreach (DictionaryEntry kvp in item as IDictionary) {
if (kvp.Value is ICollection)
result += kvp.Key + " { " + ICollectionToString(kvp.Value as ICollection) + " } ";
else
result += kvp.Key + " => " + kvp.Value + " |";
}
}
else if (item is IList) {
var serializer = new JavaScriptSerializer();
string thing = serializer.Serialize(item);
result += thing;
}
else {
// todo
}
return result;
}
catch (Exception e) {
}
return string.Empty;
}
}
// Given a column name, a row index, and a WorksheetPart, inserts a cell into the worksheet.
// If the cell already exists, returns it.
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart) {
Worksheet worksheet = worksheetPart.Worksheet;
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
string cellReference = columnName + rowIndex;
// If the worksheet does not contain a row with the specified row index, insert one.
Row row;
if (sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0) {
row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
}
else {
row = new Row() { RowIndex = rowIndex, Spans = new ListValue<StringValue>() { InnerText = "1:2" } };
sheetData.Append(row);
}
// If there is not a cell with the specified column name, insert one.
if (row.Elements<Cell>().Where(c => c.CellReference.Value == columnName + rowIndex).Count() > 0) {
return row.Elements<Cell>().Where(c => c.CellReference.Value == cellReference).First();
}
else {
// Cells must be in sequential order according to CellReference. Determine where to insert the new cell.
Cell refCell = null;
foreach (Cell cell in row.Elements<Cell>()) {
if (cell.CellReference.Value.Length == cellReference.Length) {
if (string.Compare(cell.CellReference.Value, cellReference, true) > 0) {
refCell = cell;
break;
}
}
}
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
worksheet.Save();
return newCell;
}
}
}
Running the program once will create what I believe is a valid openxml document which will open in Apache OpenOffice. Running the program twice will add two lines of which 1 is not unique to the document. This will cause the error to show up in OpenOffice, but not in the other programs (Excel Viewer/Libreoffice Calc).
Unfortunately I need to use OpenOffice as its whats installed on the computers, but I'm not sure what I am doing wrong when creating the document. Do I need to add something to the rows to indicate that it is a duplicate?
Edit: To run the code you need the DocumentFormat.OpenXML nuget package
Edit1: This only occurs when running the program twice. If I were to just append a 4 rows two of which were identical and attempt to open the file I have no issue. Note that the InsertObjectAt method also opens the document everytime (once for each cell so 4 rows by 2 cols = 8 times).
excel(XLSM) file starts with first column empty and second column with values and so on it replaces the empty column with immediate column available
XLSM FILE :Before uploading
XLSM FILE:After uploading xlsm shifts to immediate null column
how to find the range or total column without shifting
i.e:when i count column it has to display as 3(A2,B2,C2)
but it gives me total column when converting
below is the code:
private void Get_XLSM_Data(ref DataTable dt)
{
string strPath = Path.GetExtension(this.FilePath);
if (strPath != null && strPath.ToUpper() == ".XLSM")
{
using (SpreadsheetDocument spreadSheetDocument =
SpreadsheetDocument.Open(this.FilePath, true))
{
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;
var dimensionReference = workSheet.SheetDimension.Reference;
var cellTablePart = workSheet.WorksheetPart.SingleCellTablePart;
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Column> columnsDescendants = sheetData.Descendants<Column>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
var sheetIdValue = sheets.First().SheetId.Value;
// ReSharper disable once PossibleNullReferenceException
var column = workSheet.GetFirstChild<SheetData>().ChildElements.FirstOrDefault().ChildElements.Count();
if (dt.TableName == "specific table ")
{
dt.Columns.Clear();
for (int col = 1; col <= column; col++)
{
string colName = "Column" + (col);
dt.Columns.Add(colName);
}
//// START: To add Headers (First row) in data table
string[] rowData = new string[dt.Columns.Count];
int colIndex = 0;
foreach (Cell cell in rows.ElementAt(0))
{
rowData[colIndex] = GetCellValue(spreadSheetDocument, cell); colIndex++;
}
dt.Rows.Add(rowData);
//// END: To add Headers (First row) in data table
}
try
{
for (int i = 1; i < rows.Count(); i++)
{
string[] rowData = new string[dt.Columns.Count];
int col = 0;
foreach (Cell cell in rows.ElementAt(i))
{
rowData[col] = GetCellValue(spreadSheetDocument, cell); col++;
}
dt.Rows.Add(rowData);
}
}
}
}
}
public static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
if (cell != null)
{
string cellValue = cell.CellValue != null ? cell.CellValue.InnerXml : String.Empty;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
cellValue = stringTablePart.SharedStringTable.ChildElements[Int32.Parse(cellValue)].InnerText;
}
else
{
if(!string.IsNullOrEmpty(cellValue))
{
//return Convert.ToString(cellValue, CultureInfo.InvariantCulture);
return double.Parse(cellValue, CultureInfo.InvariantCulture).ToString();
}
return cellValue;
}
return cellValue;
}
return String.Empty;
}
Row row = worksheetPart.Worksheet.GetFirstChild<SheetData>().Elements<Row>().FirstOrDefault();
var totalnumberOfColumns = 0;
if (row != null)
{
var spans = row.Spans != null ? row.Spans.InnerText : "";
if (spans != String.Empty)
{
//spans.Split(':')[1];
string[] columns = spans.Split(':');
startcolumnInuse = int.Parse(columns[0]);
endColumnInUse = int.Parse(columns[1]);
totalnumberOfColumns = int.Parse(columns[1]);
}
}
Below is the screen shot to find the maximum column present through span with above code i have shared
Here i have used different excel file(XLSM)
Below is the screen shot to find the maximum column present through span
with above code i have shared
Here i have used different excel file(XLSM)
Am trying to change data for an already existing word file. I've had success in manipulating data in embedded Excel file, but somehow, this data is not being used in Word file. Moreover, in Excel file, I see the green marker before the last row, probably signifying that the last row is still not being considered for the data. Can someone please help in handling the issue?
I did referred to lot of links from ericwhite.com / openxmldeveloper.org but had no luck.
The code is below:
public void ReplaceChartValues(string placeholderCaption, System.Data.DataTable chartData, string newCaption)
{
if (wordDocument != null)
{
try
{
ChartPart target = mainDocumentPart
.ChartParts
.Where(r => r
.ChartSpace
.GetFirstChild<Chart>()
.Title
.InnerText
.StartsWith(placeholderCaption)
)
.FirstOrDefault();
if (target != null)
{
target
.ChartSpace
.GetFirstChild<Chart>()
.Title
.ChartText
.RichText
.GetFirstChild<DocumentFormat.OpenXml.Drawing.Paragraph>()
.GetFirstChild<DocumentFormat.OpenXml.Drawing.Run>()
.Text
.Text = newCaption;
ExternalData externalData =
target
.ChartSpace
.Elements<ExternalData>()
.FirstOrDefault();
if (externalData != null)
{
EmbeddedPackagePart embeddedPackagePart =
(EmbeddedPackagePart)
target
.Parts
.Where(r => r.RelationshipId == externalData.Id)
.FirstOrDefault()
.OpenXmlPart;
if (embeddedPackagePart != null)
{
using (Stream stream = embeddedPackagePart.GetStream())
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(stream, true))
{
DocumentFormat.OpenXml.Spreadsheet.Sheet worksheet = (DocumentFormat.OpenXml.Spreadsheet.Sheet)spreadsheetDocument
.WorkbookPart
.Workbook
.Sheets
.FirstOrDefault();
WorksheetPart worksheetPart = (WorksheetPart)
spreadsheetDocument
.WorkbookPart
.Parts
.Where(r => r.RelationshipId == worksheet.Id)
.FirstOrDefault()
.OpenXmlPart;
DocumentFormat.OpenXml.Spreadsheet.SheetData sheetData =
worksheetPart
.Worksheet
.Elements<DocumentFormat.OpenXml.Spreadsheet.SheetData>()
.FirstOrDefault();
var existingRows = sheetData
.Elements<DocumentFormat.OpenXml.Spreadsheet.Row>()
.Skip(1)
.ToArray();
for (int ctr = 0; ctr < existingRows.Length; ctr++)
{
sheetData
.RemoveChild<DocumentFormat.OpenXml.Spreadsheet.Row>(existingRows[ctr]);
}
for (int ctr = 0; ctr < chartData.Rows.Count; ctr++)
{
DocumentFormat.OpenXml.Spreadsheet.Row newRecord = new DocumentFormat.OpenXml.Spreadsheet.Row();
for (int ctr2 = 0; ctr2 < chartData.Columns.Count; ctr2++)
{
DocumentFormat.OpenXml.Spreadsheet.Cell newCell = new DocumentFormat.OpenXml.Spreadsheet.Cell();
DocumentFormat.OpenXml.Spreadsheet.CellValue newCellValue = new DocumentFormat.OpenXml.Spreadsheet.CellValue();
if (chartData.Columns[ctr2].DataType == typeof(DateTime))
{
newCellValue.Text = Convert.ToDateTime(chartData.Rows[ctr][ctr2]).ToString("MM/dd/yyyy");
newCell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.Date;
}
else if (chartData.Columns[ctr2].DataType == typeof(decimal))
{
newCellValue.Text = chartData.Rows[ctr][ctr2].ToString();
newCell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.Number;
}
else
{
newCellValue.Text = chartData.Rows[ctr][ctr2].ToString();
newCell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.String;
}
newCell.AppendChild(newCellValue);
newRecord.AppendChild(newCell);
}
sheetData.AppendChild(newRecord);
}
spreadsheetDocument.Save();
}
}
}
}
}
}
catch (Exception)
{
}
finally
{
// Save the document.
mainDocumentPart.Document.Save();
}
}
}
Spent lot of hours behind this and here is the code that helped:
public void ReplaceChartValuesLinearChart(string placeholderCaption, System.Data.DataTable chartData, string newCaption)
{
if (wordDocument != null)
{
try
{
// Get the exact Chart Part where Caption matches the place holder value.
ChartPart target = mainDocumentPart
.ChartParts
.Where(r => r
.ChartSpace
.GetFirstChild<Chart>()
.Title
.InnerText
.StartsWith(placeholderCaption)
)
.FirstOrDefault();
if (target != null)
{
// Set the new caption.
target
.ChartSpace
.GetFirstChild<Chart>()
.Title
.ChartText
.RichText
.GetFirstChild<DocumentFormat.OpenXml.Drawing.Paragraph>()
.GetFirstChild<DocumentFormat.OpenXml.Drawing.Run>()
.Text
.Text = newCaption;
// Update all NumberingCache values to reflect total number of records.
foreach (NumberingCache currentNumberingCache in target.ChartSpace.Descendants<NumberingCache>())
{
currentNumberingCache.PointCount = new PointCount() { Val = (DocumentFormat.OpenXml.UInt32Value)(UInt32)chartData.Rows.Count };
currentNumberingCache.RemoveAllChildren<NumericPoint>();
}
// Set the Numeric Point values with formats and add to the appropriate NumberingCache.
for (int ctr = 0; ctr < chartData.Rows.Count; ctr++)
{
// First Range - contains date.
NumericPoint newNumericPoint = new NumericPoint();
newNumericPoint.Index = new DocumentFormat.OpenXml.UInt32Value((uint)ctr);
newNumericPoint.FormatCode = "[$-409]ddmmmyyyy";
newNumericPoint.NumericValue = new NumericValue(chartData.Rows[ctr][0].ToString());
target
.ChartSpace
.Descendants<NumberingCache>()
.ToArray()[0]
.AppendChild(newNumericPoint);
// Third Range - contains date.
newNumericPoint = new NumericPoint();
newNumericPoint.Index = new DocumentFormat.OpenXml.UInt32Value((uint)ctr);
newNumericPoint.FormatCode = "[$-409]ddmmmyyyy";
newNumericPoint.NumericValue = new NumericValue(chartData.Rows[ctr][0].ToString());
target
.ChartSpace
.Descendants<NumberingCache>()
.ToArray()[2]
.AppendChild(newNumericPoint);
// Second Range - contains reference data.
if (chartData.Rows[ctr][2] != DBNull.Value)
{
newNumericPoint = new NumericPoint();
newNumericPoint.Index = new DocumentFormat.OpenXml.UInt32Value((uint)ctr);
newNumericPoint.FormatCode = "0.00%";
newNumericPoint.NumericValue = new NumericValue(chartData.Rows[ctr][2].ToString());
target
.ChartSpace
.Descendants<NumberingCache>()
.ToArray()[1]
.AppendChild(newNumericPoint);
}
// Second Range - contains current data.
if (chartData.Rows[ctr][3] != DBNull.Value)
{
newNumericPoint = new NumericPoint();
newNumericPoint.Index = new DocumentFormat.OpenXml.UInt32Value((uint)ctr);
newNumericPoint.FormatCode = "0.00%";
newNumericPoint.NumericValue = new NumericValue(chartData.Rows[ctr][3].ToString());
target
.ChartSpace
.Descendants<NumberingCache>()
.ToArray()[3]
.AppendChild(newNumericPoint);
}
}
// Update all variable length formula to point to updated number of rows.
foreach (var currentFormula in target.ChartSpace.Descendants<Formula>())
{
if (currentFormula.Text.Contains(":"))
{
currentFormula.Text =
currentFormula.Text.Substring(0, currentFormula.Text.LastIndexOf("$") + 1)
+ (chartData.Rows.Count + 1).ToString();
}
}
// Get handle to ExternalData for accessing embedded Excel document.
ExternalData externalData =
target
.ChartSpace
.Elements<ExternalData>()
.FirstOrDefault();
if (externalData != null)
{
// Get handle to Package Part containing excel document.
EmbeddedPackagePart embeddedPackagePart =
(EmbeddedPackagePart)
target
.Parts
.Where(r => r.RelationshipId == externalData.Id)
.FirstOrDefault()
.OpenXmlPart;
if (embeddedPackagePart != null)
{
// Get handle to Stream for modifying data.
using (Stream stream = embeddedPackagePart.GetStream())
{
// Open Excel for manipulation.
using (SpreadsheetDocument spreadsheetDocument =
SpreadsheetDocument.Open(stream, true))
{
// Get handle to first sheet.
DocumentFormat
.OpenXml
.Spreadsheet
.Sheet worksheet = (DocumentFormat.OpenXml.Spreadsheet.Sheet)
spreadsheetDocument
.WorkbookPart
.Workbook
.Sheets
.FirstOrDefault();
// Get handle to first worksheet.
WorksheetPart worksheetPart = (WorksheetPart)
spreadsheetDocument
.WorkbookPart
.Parts
.Where(r => r.RelationshipId == worksheet.Id)
.FirstOrDefault()
.OpenXmlPart;
// Set Table range on the first worksheet.
worksheetPart
.TableDefinitionParts
.FirstOrDefault()
.Table
.Reference
.Value = "A1:D" + (chartData.Rows.Count + 1).ToString();
// Get handle to access entire sheet data.
DocumentFormat
.OpenXml
.Spreadsheet
.SheetData sheetData =
worksheetPart
.Worksheet
.Elements<DocumentFormat.OpenXml.Spreadsheet.SheetData>()
.FirstOrDefault();
// Select all data rows.
var existingRows = sheetData
.Elements<DocumentFormat.OpenXml.Spreadsheet.Row>()
.Skip(1)
.ToArray();
// Remove all existing data rows.
for (int ctr = 0; ctr < existingRows.Length; ctr++)
{
sheetData
.RemoveChild<DocumentFormat.OpenXml.Spreadsheet.Row>(existingRows[ctr]);
}
// Create new rows.
for (int ctr1 = 0; ctr1 < chartData.Rows.Count; ctr1++)
{
DocumentFormat
.OpenXml
.Spreadsheet
.Row newRecord = new
DocumentFormat
.OpenXml
.Spreadsheet.Row();
// Set values and formats for each cell for new row.
for (int ctr2 = 0; ctr2 < chartData.Columns.Count; ctr2++)
{
// Create a new cell.
DocumentFormat
.OpenXml
.Spreadsheet
.Cell newCell = new
DocumentFormat
.OpenXml
.Spreadsheet
.Cell();
// Create a new cell value for holding actual value of the cell.
DocumentFormat
.OpenXml
.Spreadsheet
.CellValue newCellValue = new
DocumentFormat
.OpenXml
.Spreadsheet
.CellValue();
// Set appropriate Style, Data Type and value for the cell.
switch (ctr2)
{
case 0:
newCell.StyleIndex = new
DocumentFormat
.OpenXml
.UInt32Value((uint)2);
newCellValue.Text =
chartData.Rows[ctr1][ctr2].ToString();
break;
case 1:
newCellValue.Text =
GetSharedStringIndex(
spreadsheetDocument
.WorkbookPart
.SharedStringTablePart,
chartData.Rows[ctr1][ctr2].ToString());
newCell.StyleIndex = new
DocumentFormat
.OpenXml
.UInt32Value((uint)2);
newCell.DataType =
DocumentFormat
.OpenXml
.Spreadsheet
.CellValues
.SharedString;
break;
case 2:
case 3:
newCellValue.Text =
chartData.Rows[ctr1][ctr2].ToString();
if (chartData.Rows[ctr1][ctr2] != DBNull.Value &&
chartData.Rows[ctr1][ctr2].ToString().Trim().Length > 0)
{
newCell.StyleIndex = new
DocumentFormat
.OpenXml
.UInt32Value((uint)3);
}
else
{
newCell.StyleIndex = new
DocumentFormat
.OpenXml
.UInt32Value((uint)1);
}
break;
}
// Append newly created cell value to the cell.
newCell.AppendChild(newCellValue);
// Append newly created cell to the Row.
newRecord.AppendChild(newCell);
}
// Append newly created row to the Excel sheet.
sheetData.AppendChild(newRecord);
}
spreadsheetDocument.Save();
}
}
}
}
}
}
catch (Exception)
{
}
finally
{
// Save the document.
mainDocumentPart.Document.Save();
}
}
}
private string GetSharedStringIndex(SharedStringTablePart sharedStringTablePart, string valueToSearch)
{
int counter = 0;
// Return index if item already exists.
foreach (DocumentFormat.OpenXml.Spreadsheet.SharedStringItem currentItem in sharedStringTablePart.SharedStringTable.Elements<DocumentFormat.OpenXml.Spreadsheet.SharedStringItem>())
{
if (currentItem.InnerText == valueToSearch)
{
return counter.ToString();
}
counter++;
}
// The text does not exist in the part. Create the SharedStringItem and return its index.
sharedStringTablePart.SharedStringTable.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(valueToSearch)));
sharedStringTablePart.SharedStringTable.Save();
return counter.ToString();
}
I have the following piece of code in a much larger OpenXML Excel reader. This reader grabs the information assigns to a dataset and then is displayed in a datagridview:
public static DataTable ExtractExcelSheetValuesToDataTable(string xlsxFilePath, string sheetName, int startingRow) {
DataTable dt = new DataTable();
using (SpreadsheetDocument myWorkbook = SpreadsheetDocument.Open(xlsxFilePath, true)) {
//Access the main Workbook part, which contains data
WorkbookPart workbookPart = myWorkbook.WorkbookPart;
WorksheetPart worksheetPart = null;
if (!string.IsNullOrEmpty(sheetName)) {
Sheet ss = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).SingleOrDefault<Sheet>();
worksheetPart = (WorksheetPart)workbookPart.GetPartById(ss.Id);
} else {
worksheetPart = workbookPart.WorksheetParts.FirstOrDefault();
}
SharedStringTablePart stringTablePart = workbookPart.SharedStringTablePart;
if (worksheetPart != null) {
Row lastRow = worksheetPart.Worksheet.Descendants<Row>().LastOrDefault();
#region ColumnCreation
//Returns the columns - come back to this later - may be able to modify this to have
//A checkbox "Column names in first row"
Row firstRow = worksheetPart.Worksheet.Descendants<Row>().FirstOrDefault();
int columnInt = 0;
//if (firstRow != null)
//{
foreach (Cell c in firstRow.ChildElements)
{
string value = GetValue(c, stringTablePart);
dt.Columns.Add(columnInt + ": " + value);
columnInt++;
}
//}
#endregion
#region Create Rows
//if (lastRow != null)
//{
//lastRow.RowIndex;
for (int i = startingRow; i <= 150000; i++)
{
DataRow dr = dt.NewRow();
bool empty = true;
Row row = worksheetPart.Worksheet.Descendants<Row>().Where(r => i == r.RowIndex).FirstOrDefault();
int j = 0;
if (row != null)
{
foreach (Cell c in row.ChildElements)
{
//Get cell value
string value = GetValue(c, stringTablePart);
if (!string.IsNullOrEmpty(value) && value != "")
empty = false;
dr[j] = value;
j++;
if (j == dt.Columns.Count)
break;
}
if (empty)
break;
dt.Rows.Add(dr);
}
}
}
#endregion
}
// }
return dt;
}
public static string GetValue(Cell cell, SharedStringTablePart stringTablePart) {
if (cell.ChildElements.Count == 0) return null;
//get cell value
string value = cell.ElementAt(0).InnerText;//CellValue.InnerText;
//Look up real value from shared string table
if ((cell.DataType != null) && (cell.DataType == CellValues.SharedString))
value = stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
return value;
}
public void GetSheetInfo(string fileName)
{
Sheets theSheets = null;
// Open file as read-only.
using (SpreadsheetDocument mySpreadsheet = SpreadsheetDocument.Open(fileName, false))
{
S sheets = mySpreadsheet.WorkbookPart.Workbook.Sheets;
WorkbookPart wbPart = mySpreadsheet.WorkbookPart;
theSheets = wbPart.Workbook.Sheets;
foreach (Sheet item in theSheets)
{
cmbSheetSelect.Items.Add(item.Name);
}
}
}
This has worked for basic spreadsheets but as I try to read more advanced ones I get a problem or two.
Firstly, I have a worksheet that has 5 columns:see here
However when I run my program it only returns the first 4 columns and not column E and all its data.
My second question would is it possible using that code (or a variation of it) to be able to specify the line I want the program to read as the datagridview column heading?
In case anyone needs this I found that changing:
Row firstRow = worksheetPart.Worksheet.Descendants<Row>().FirstOrDefault();
To
Row firstRow = worksheetPart.Worksheet.Descendants<Row>().ElementAtOrDefault(columnIndex)
Worked. With columnIndex being a variable I can change based on the sheet selected.
I am writing a C# console application that will read values from an excel spreadsheet using OpenXML and create a DataTable. The app is able to read all values except those cells which contain a dropdown list. Is there a way for OpenXML to read these cells and determine which value is selected? Any suggestions are greatly appreciated. Thanks in advance.
Current Code:
public static string GetValue(Cell cell, SharedStringTablePart stringTablePart)
{
if (cell.ChildElements.Count == 0) return null;
//get cell value
string value = cell.ElementAt(0).InnerText;//CellValue.InnerText;
//Look up real value from shared string table
if ((cell.DataType != null) && (cell.DataType == CellValues.SharedString))
value = stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
return value;
}
public static void ReadData(string xlsxFilePath, string sheetName)
{
DataTable dt = new DataTable();
using (SpreadsheetDocument myWorkbook = SpreadsheetDocument.Open(xlsxFilePath, true))
{
//Access the main Workbook part, which contains data
WorkbookPart workbookPart = myWorkbook.WorkbookPart;
WorksheetPart worksheetPart = null;
if (!string.IsNullOrEmpty(sheetName))
{
Sheet ss = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).SingleOrDefault<Sheet>();
worksheetPart = (WorksheetPart)workbookPart.GetPartById(ss.Id);
}
else
{
worksheetPart = workbookPart.WorksheetParts.FirstOrDefault();
}
SharedStringTablePart stringTablePart = workbookPart.SharedStringTablePart;
if (worksheetPart != null)
{
Row lastRow = worksheetPart.Worksheet.Descendants<Row>().LastOrDefault();
IEnumerable<Row> firstRows = worksheetPart.Worksheet.Descendants<Row>().Skip(10);
Row firstRow = firstRows.FirstOrDefault();
int numColumns = 0;
//Row firstRow = worksheetPart.Worksheet.Descendants<Row>().FirstOrDefault();
if (firstRow != null)
{
foreach (Cell c in firstRow.ChildElements)
{
string value = GetValue(c, stringTablePart);
dt.Columns.Add(value);
numColumns++;
}
}
if (lastRow != null)
{
for (int i = 11; i <= lastRow.RowIndex; i++)
{
DataRow dr = dt.NewRow();
bool empty = true;
Row row = worksheetPart.Worksheet.Descendants<Row>() .Where(r => i == r.RowIndex).FirstOrDefault();
int j = 0;
if (row != null)
{
foreach (Cell c in row.ChildElements)
{
//Get cell value
string value = GetValue(c, stringTablePart);
if (string.IsNullOrEmpty(value) && value == " ")
dr[j] = "";
//if (!string.IsNullOrEmpty(value) && value != " ")
// empty = false;
else
dr[j] = value;
Console.Write(dr[j] + "\t");
j++;
if (j == numColumns-1)
{
Console.Write("\n");
break;
}
}
//if (empty)
// break;
dt.Rows.Add(dr);
}
}
}
}
}
}