Add Cell and Row in openXML - c#

I have pre define excel format i need to pass the data to excel.I'm able to get the particular sheet .But don't know how to pass the data to cell.
var excelDocument = new ExcelDocument();
var fileName = Guid.NewGuid();
string filePath = HttpContext.Current.Server.MapPath("~/Uploads/TemplateFiles/test.xlsx");
using (SpreadsheetDocument document =
SpreadsheetDocument.Open(filePath, false))
{
WorkbookPart workbookPart = document.WorkbookPart;
Workbook workbook = document.WorkbookPart.Workbook;
string sheetName = workbookPart.Workbook.Descendants<Sheet>().ElementAt(1).Name;
IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == "Census Template for Import");
if (sheets.Count() == 0)
{
// The specified worksheet does not exist.
return null;
}
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheets.First().Id);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
var excelRows = sheetData.Descendants<DocumentFormat.OpenXml.Spreadsheet.Row>().ToList();
int rowindex = 10;
foreach (var item in census)
{
//how to write the data in cell
rowindex++;
}
worksheetPart.Worksheet.Save();
workbookPart.Workbook.Save();
document.Close();
//worksheetPart.Worksheet.Save();
}
return filePath;

Here is a method for getting a cell or adding a new one, if the cell does not exists, when you know both the row and column indexes.
Note that:
rowIndex and columnIndex should start with 1
property RowIndex of a Row should be initialized during the creation of the row
property CellReference of a Cell should be initialized during the creation of the cell
If RowIndex or CellReference is null, then NullReferenceException will be thrown.
private Cell InsertCell(uint rowIndex, uint columnIndex, Worksheet worksheet)
{
Row row = null;
var sheetData = worksheet.GetFirstChild<SheetData>();
// Check if the worksheet contains a row with the specified row index.
row = sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);
if (row == null)
{
row = new Row() { RowIndex = rowIndex };
sheetData.Append(row);
}
// Convert column index to column name for cell reference.
var columnName = GetExcelColumnName(columnIndex);
var cellReference = columnName + rowIndex; // e.g. A1
// Check if the row contains a cell with the specified column name.
var cell = row.Elements<Cell>()
.FirstOrDefault(c => c.CellReference.Value == cellReference);
if (cell == null)
{
cell = new Cell() { CellReference = cellReference };
if (row.ChildElements.Count < columnIndex)
row.AppendChild(cell);
else
row.InsertAt(cell, (int)columnIndex);
}
return cell;
}
Here you will find the code of GetExcelColumnName() method.

Can't tell if its a new file your creating or appending into an existing one but:
spreadSheet.WorkbookPart.WorksheetParts.First().Worksheet.First().AppendChild(new Row());
sheet.First().Last().AppendChild(new Cell() { CellValue = new CellValue("test") });
Should work for both cases but the new cell will be put on the last active row in the first sheet.

I had the same issue that you had and this article How to: Insert text into a cell in a spreadsheet document (Open XML SDK). I guess you need to insert a new Cell object into your worksheet and then insert the specified data (assuming it is a string or that it has already being cast into a string) into that cell.

Seems you define rowindex=10, there are two way to add rows.
If row 10 is last row in your excel then you can simply append new row like:
foreach (var item in census)
{
//how to write the data in cell
Row row = new Row();
row.RowIndex = (UInt32)rowindex;
Cell cell = new Cell()
{
DataType = CellValues.String,
CellValue = new CellValue("value")
};
row.Append(cell);
sheetData.Append(row);
rowindex++;
}
If there are rows after row 10 then you have to use insert,then manually change rows and cells after row 10
index to the right index value like:
foreach (var item in census)
{
//how to write the data in cell
Row refRow = GetRow(sheetData, rowIndex);
++rowIndex;
Cell cell1 = new Cell() { CellReference = "A" + rowIndex };
CellValue cellValue1 = new CellValue();
cellValue1.Text = "";
cell1.Append(cellValue1);
Row newRow = new Row()
{
RowIndex = rowIndex
};
newRow.Append(cell1);
for (int i = (int)rowIndex; i <= sheetData.Elements<Row>().Count(); i++)
{
var row = sheetData.Elements<Row>().Where(r => r.RowIndex.Value == i).FirstOrDefault();
row.RowIndex++;
foreach (Cell c in row.Elements<Cell>())
{
string refer = c.CellReference.Value;
int num = Convert.ToInt32(Regex.Replace(refer, #"[^\d]*", ""));
num++;
string letters = Regex.Replace(refer, #"[^A-Z]*", "");
c.CellReference.Value = letters + num;
}
}
sheetData.InsertAfter(newRow, refRow);
rowindex++;
}
static Row GetRow(SheetData wsData, UInt32 rowIndex)
{
var row = wsData.Elements<Row>().
Where(r => r.RowIndex.Value == rowIndex).FirstOrDefault();
if (row == null)
{
row = new Row();
row.RowIndex = rowIndex;
wsData.Append(row);
}
return row;
}
This is a prototype. You might need to change some code or variable name to fit your project.
References:
append rows in Excel by OpenXML
insert rows in Excel by OpenXML

Related

Insert new row in Excel using OpenXml with specified columns

This code is working fine but i want to do that i have 3 sheets in excel file and all sheets
have different columns size , suppose first sheet have 5 columns second have 3 and third have
7 so on the basis of sheets i want to insert new row. Like if first sheet have 5 columns then data
insert only in 5 columns if second have 3 then only 3 values insert like that. So, can any one guide
me to do that.
using(FileStream fs = new FileStream(filepath, Filemode.Open, FileAccess.ReadWrite,
FileShare.ReadWrite))
{
SpreadSheetDocument document = SpreadsheetDocument.Open(fs, false);
SharedStringTable sharedStringTable =
document.WorkbookPart.SharedStringTablePart.SharedStringTable;
string cellValue = null;
foreach (WorksheetPart worksheetPart in document.WorkbookPart.WorksheetParts.Reverse())
{
int cnt = document.WorkbookPart.WorksheetParts.count();
foreach (SheetData sheetData in worksheetPart.Worksheet.Elements<SheetData>())
{
if (sheetData.HasChildren)
{
foreach (Row row in sheetData.Elements<Row>())
{
foreach (Cell cell in row.Elements<Cell>())
{
Row row = new Row();
row.Append(ConstructCell("firstvalue",CellValues.String));
sheetData.AppendChild(row);
WorksheetPart.Worksheet.Save();
}
}
}
}
}
}
document.Close();
Public static Cell ConstructCell(string value , CellValues datatype)
{
return new Cell(){CellValue = new CellValue(value),DataType = new EnumValue<CellValues>
(datatype)};
}
I done it.
//Replace this from
Row row = new Row();
row.Append(ConstructCell("firstvalue",CellValues.String));
sheetData.AppendChild(row);
WorksheetPart.Worksheet.Save();
//To this one ::
var rows = sheetData.Elements<Row>;
int rwCnt = rows.Count();
var clCnt = rows.First().ChildElements.Count();
for(int i = 0; i < 1 ; i++)
{
Row row = new Row();
for(j = 0; j < clCnt; j++)
{
row.Append(
ConstructCell("firstvalue",CellValues.String)
)
}
sheetData.AppendChild(row);
WorksheetPart.Worksheet.Save();
}

when converting excel to data-table using OpenXML replaces first null column with second column which contains data in c#

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)

How to get a specific row by worksheet and rowindex in excel file

I need to choose a specific cell in my excel file via using openXML:
Worksheet workSheet = workSheetPart.Worksheet;
Cell cell = GetCell(workSheet, "B", 2);
private static Cell GetCell(Worksheet worksheet,
string columnName, uint rowIndex)
{
Row row = GetRow(worksheet, rowIndex);
if (row == null)
return null;
return row.Elements<Cell>().Where(c => string.Compare
(c.CellReference.Value, columnName +
rowIndex, true) == 0).First();
}
private static Row GetRow(Worksheet worksheet, uint rowIndex)
{
var test = worksheet.GetFirstChild<SheetData>().
Elements<Row>().Where(r => r.RowIndex == rowIndex).First(); //Here is the problem.
return worksheet.GetFirstChild<SheetData>().
Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
}
When debugging I have noticed that RowIndex is null, so this is causing the problem I guess in the linq query
Use ElementAt...
var test = worksheet.GetFirstChild<SheetData>().Elements<Row>().ElementAt(rowIndex);
When you create a row, you have to set the Row index value for each row you added to the excel.
Row headerRow = new Row() { RowIndex = new UInt32Value(1) }; //Here I used as 1(one), use accordingly
When you create a cell, you have to set the CellReference value for each cell you created.
Cell cell = new Cell() { };
cell.CellReference = new StringValue("A1");
cell.DataType = CellValues.String;
cell.CellValue = new CellValue("Test value");
If the Row index and cell reference is not set, the values will be null when you query among it.
Some other useful methods for using in excel
//To get the CellReference
private static string GetExcelCellReference(uint columnNumber, uint rowNumber)
{
return $"{GetExcelColumnName(columnNumber)}{rowNumber}";
}
//To get the excel column name using column number
private static string GetExcelColumnName(uint columnNumber)
{
int dividend = (int)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;
}
//To get the excel column name from cellname or cellreference number
private static string GetColumnName(string cellName)
{
// Create a regular expression to match the column name portion of the cell name.
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellName);
return match.Value;
}
//To get the row index from cell name or cell reference number
private static uint GetRowIndex(string cellName)
{
// Create a regular expression to match the row index portion the cell name.
Regex regex = new Regex(#"\d+");
Match match = regex.Match(cellName);
return uint.Parse(match.Value);
}

How to add new row with cell data and styles in Excel using OpenXML and C#

How to add new rows with cell data and styles in Excel using OpenXML and C#
Though it was a question from my side but not anymore. I'll mention my R&D and code stuff to which resolved my problem.
public override void AddExcelRows(string[] bufData, int cReport, int cSection, int nrow, bool insertRow)
{
int rowIndex;
int colIndex;
rowIndex = //some number
colIndex = //some number
Sheet sheet = wbPart.Workbook.Descendants<Sheet>().Where((s) => s.Name == currentSheetName).FirstOrDefault();
WorksheetPart worksheetPart = wbPart.GetPartById(sheet.Id) as WorksheetPart;
SharedStringTablePart shareStringPart = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
for (int colOffset = 0; colOffset <= some number; colOffset++)
{
if (bufData[colOffset] != null)
{
int index = InsertSharedStringItem(bufData[colOffset], shareStringPart);
var columnName = GetExcelColumnName(colIndex + colOffset);
Cell cell = InsertCellInWorksheet(columnName, rowIndex, worksheetPart);
if (cell.CellValue !=null && cell.CellValue.InnerText == bufData[colOffset])//if same value is already present in current cell then skip writign again. it was causing issue writng [kW] for project Technical report.
{
continue;
}
cell.CellValue = new CellValue(index.ToString());
cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
}
}
if (insertRow)
{
uint nextRowIndex = (uint)rowIndex + 1; //Add min 3 rows in excel with styles (border line)
Row oldRow = sheetData.Elements<Row>().Where(r => r.RowIndex == nextRowIndex).First();
var newRow = oldRow.CopyToLine((uint)nextRowIndex, sheetData);
}
wbPart.Workbook.Save();
}
Helper methods:
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;
}
Below two methods were reused from: https://msdn.microsoft.com/en-us/library/office/cc861607.aspx
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;
}
// 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, int 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 = null;
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 = (uint)rowIndex };
sheetData.InsertAt(new Row(), rowIndex);
}
// 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 (string.Compare(cell.CellReference.Value, cellReference, true) > 0)
{
refCell = cell;
break;
}
}
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
worksheetPart.Worksheet.Save();
return newCell;
}
}
Then you need to add an extension Method for Adding new row with styles
public static class ExtensionClass
{
//A method for copying a row and insert it:
//Copy an existing row and insert it
//We don't need to copy styles of a refRow because a CloneNode() or Clone() methods do it for us
public static Row CopyToLine(this Row refRow, uint rowIndex, SheetData sheetData)
{
uint newRowIndex;
var newRow = (Row)refRow.CloneNode(true);
// Loop through all the rows in the worksheet with higher row
// index values than the one you just added. For each one,
// increment the existing row index.
IEnumerable<Row> rows = sheetData.Descendants<Row>().Where(r => r.RowIndex.Value >= rowIndex);
foreach (Row row in rows)
{
newRowIndex = System.Convert.ToUInt32(row.RowIndex.Value + 1);
foreach (Cell cell in row.Elements<Cell>())
{
// Update the references for reserved cells.
string cellReference = cell.CellReference.Value;
cell.CellReference = new StringValue(cellReference.Replace(row.RowIndex.Value.ToString(), newRowIndex.ToString()));
cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
}
// Update the row index.
row.RowIndex = new UInt32Value(newRowIndex);
}
sheetData.InsertBefore(newRow, refRow);
return newRow;
}
}

Get Column number from cell value, openxml

I have an Excel wherein i want to get the column number for eg the below image :
In the above image , i know that the records will appear on the 1st row , but i am unsure of the Column number. In above example the column value : "Quantity" appears on "D1". I know the row number how can i find the column number ("D" in the above case) using OPEN XML, as the column name quantity might appear anywhere in the excel and i need to find the corresponding values of only quantity.
Unfortunately there's not a single method you can call to find the correct cell. Instead you'll need to iterate over the cells to find the matching text. To complicate things slightly, the value in the cell is not always the actual text. Instead strings can be stored in the SharedStringTablePart and the value of the cell is an index into the contents of that table.
Something like the following should do what you're after:
private static string GetCellReference(string filename, string sheetName, int rowIndex, string textToFind)
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(filename, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
//get the correct sheet
Sheet sheet = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).First();
if (sheet != null)
{
WorksheetPart worksheetPart = workbookPart.GetPartById(sheet.Id) as WorksheetPart;
SharedStringTablePart stringTable = workbookPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
Row row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
if (row != null)
{
foreach (Cell c in row.Elements<Cell>())
{
string cellText;
if (c.DataType == CellValues.SharedString)
{
//the value will be a number which is an index into the shared strings table
int index = int.Parse(c.CellValue.InnerText);
cellText = stringTable.SharedStringTable.ElementAt(index).InnerText;
}
else
{
//just take the value from the cell (note this won't work for dates and other types)
cellText = c.CellValue.InnerText;
}
if (cellText == textToFind)
{
return c.CellReference;
}
}
}
}
}
return null;
}
This can then be called like this:
string cellReference = GetCellReference(#"c:\temp\test.xlsx", "Sheet1", 1, "Quantity");
Console.WriteLine(cellReference); //prints D1 for your example
If you just want D rather than D1 you can use a simple regex to remove the numbers:
private static string GetColumnName(string cellReference)
{
if (cellReference == null)
return null;
return Regex.Replace(cellReference, "[0-9]", "");
}
And then use it like this:
string cellReference = GetCellReference(#"c:\temp\test.xlsx", "Sheet1", 1, "Quantity");
Console.WriteLine(GetColumnName(cellReference)); //prints D for your example

Categories