Open XML Sheetview Right to left - c#

I need to add SheetView settings in ExporToExcel Class. But after export, it's showing that Excel Cell Content is empty even though the worksheet is created and SheetView settings are set. I checked all of the posts on this site and other site's as well, but I was not successful. Every time I am getting a Document corrupted message or the spreadsheet is empty.
private static void WriteExcelFile(DataSet ds, SpreadsheetDocument spreadsheet)
{
spreadsheet.AddWorkbookPart();
spreadsheet.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
spreadsheet.WorkbookPart.Workbook.Append(new BookViews(new WorkbookView()));
WorkbookStylesPart workbookStylesPart = spreadsheet.WorkbookPart.AddNewPart<WorkbookStylesPart>("rIdStyles");
//Stylesheet stylesheet = new Stylesheet();
workbookStylesPart.Stylesheet = CreateStylesheet();
uint worksheetNumber = 1;
Sheets sheets = spreadsheet.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
foreach (DataTable dt in ds.Tables)
{
string worksheetName = dt.TableName;
WorksheetPart newWorksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
Sheet sheet = new Sheet() { Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart), SheetId = worksheetNumber, Name = worksheetName };
newWorksheetPart.Worksheet = new Worksheet(new SheetViews(new SheetView() { WorkbookViewId=0,RightToLeft=true}),new SheetData());
newWorksheetPart.Worksheet.Save();
sheets.Append(sheet);
WriteDataTableToExcelWorksheet(dt, newWorksheetPart);
worksheetNumber++;
}
spreadsheet.WorkbookPart.Workbook.Save();
}
private static void WriteDataTableToExcelWorksheet(DataTable dt, WorksheetPart worksheetPart)
{
OpenXmlWriter writer = OpenXmlWriter.Create(worksheetPart, Encoding.ASCII);
writer.WriteStartElement(new Worksheet());
writer.WriteStartElement(new SheetData());
string cellValue = "";
int numberOfColumns = dt.Columns.Count;
bool[] IsNumericColumn = new bool[numberOfColumns];
bool[] IsDateColumn = new bool[numberOfColumns];
string[] excelColumnNames = new string[numberOfColumns];
for (int n = 0; n < numberOfColumns; n++)
excelColumnNames[n] = GetExcelColumnName(n);
//
// Create the Header row in our Excel Worksheet
//
uint rowIndex = 1;
writer.WriteStartElement(new Row { RowIndex = rowIndex });
for (int colInx = 0; colInx < numberOfColumns; colInx++)
{
DataColumn col = dt.Columns[colInx];
AppendTextCell(excelColumnNames[colInx] + "1", col.ColumnName, ref writer);
IsNumericColumn[colInx] = (col.DataType.FullName == "System.Decimal") || (col.DataType.FullName == "System.Int32") || (col.DataType.FullName == "System.Double") || (col.DataType.FullName == "System.Single");
IsDateColumn[colInx] = (col.DataType.FullName == "System.DateTime");
}
writer.WriteEndElement(); // End of header "Row"
double cellNumericValue = 0;
foreach (DataRow dr in dt.Rows)
{
++rowIndex;
writer.WriteStartElement(new Row { RowIndex = rowIndex });
for (int colInx = 0; colInx < numberOfColumns; colInx++)
{
cellValue = dr.ItemArray[colInx].ToString();
cellValue = ReplaceHexadecimalSymbols(cellValue);
if (IsNumericColumn[colInx])
{
cellNumericValue = 0;
if (double.TryParse(cellValue, out cellNumericValue))
{
cellValue = cellNumericValue.ToString();
AppendNumericCell(excelColumnNames[colInx] + rowIndex.ToString(), cellValue, ref writer);
}
}
else if (IsDateColumn[colInx])
{
// This is a date value.
DateTime dtValue;
string strValue = "";
if (DateTime.TryParse(cellValue, out dtValue))
strValue = dtValue.ToShortDateString();
AppendTextCell(excelColumnNames[colInx] + rowIndex.ToString(), strValue, ref writer);
}
else
{
AppendTextCell(excelColumnNames[colInx] + rowIndex.ToString(), cellValue, ref writer);
}
}
writer.WriteEndElement(); // End of Row
}
writer.WriteEndElement(); // End of SheetData
writer.WriteEndElement(); // End of worksheet
writer.Close();
}

The code to make the sheet read RightToLeft sheet was correct. However, the code for WriteDataTableToExcelWorksheet and CreateStylesheet was not complete and had some problems.
I was able to find the original code for the WriteDataTableToExcelWorksheet at this link. Then I reversed engineered a plain Excel file with the Open XML Productivity Tool to C# code and reused a method to create a new Stylesheet.
The excel file I generated with this code is shown here:
I've pushed this new code to GitHub. There is a wrapper WPF application that guides where to generate the file on your machine.
Here is a guide on installing the Open XML Productivity Tool.

Related

Is there anyway to upload a large Excel into a database quickly using C#?

I am trying to upload a large Ms Excel file into a relational database using C#.
Using following code:
But the process takes a very long time to read the excel (Approximated 45 hours to read an excel file with 185,000 record).
public static DataTable GetSpreadsheetWorkbookSheet(string filepath)
{
DataTable dataTable = new DataTable();
List<string> sheetList = new List<string>();
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(filepath, true))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
int sheetCount = workbookPart.Workbook.Descendants<Sheet>().Count();
string relationshipId = "";
dataTable.Columns.Add("Sheet");
dataTable.Columns.Add("Path");
for (int i = 0; i < sheetCount; i++)
{
DataRow dataRow = dataTable.NewRow();
string sheetName = workbookPart.Workbook.Descendants<Sheet>().ElementAt(i).Name;
relationshipId = workbookPart.Workbook.Descendants<Sheet>().ElementAt(i).Id;
dataTable.Rows.Add(sheetName, filepath);
}
}
return dataTable;
}
public static DataTable CreateSpreadsheetWorkbook(string filepath, string sheetName)
{
DataTable dataTable = new DataTable();
List<string> sheetList = new List<string>();
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(filepath, true))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string relationshipId = "";
int x = sheets.ToList().Count;
if (sheets.ToList().Count > 1)
{
relationshipId = sheets.ToList().Find(io => io.Name.ToString().Equals(sheetName)).Id;
}
else
{
relationshipId = sheets.ToList().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)
{
int t = row.Descendants<Cell>().Count();
DataRow dataRow = dataTable.NewRow();
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
Thread.Sleep(1);
dataRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
Thread.Sleep(1);
}
dataTable.Rows.Add(dataRow);
Thread.Sleep(1);
}
}
dataTable.Rows.RemoveAt(0);
return dataTable;
}
private static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
string value = cell.InnerText;
if (cell.DataType != null && (cell.DataType.Value == CellValues.SharedString))
{
string txt = stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText.ToString();
Thread.Sleep(1);
return txt;
}
else if (cell.DataType != null && (cell.DataType.Value == CellValues.String))
{
string txt = value;
Thread.Sleep(1);
return txt;
}
else if (cell.DataType != null && (cell.DataType.Value == CellValues.InlineString))
{
string txt = stringTablePart.ToString();
Thread.Sleep(1);
return txt;
}
else if (cell.DataType == null)
{
string txt = value;
Thread.Sleep(1);
return txt;
}
else
{
if (String.IsNullOrEmpty(value) || String.IsNullOrWhiteSpace(value))
{
value = "0";
}
return Convert.ToDecimal(value).ToString("N4");
}
}
I need to upload the file quickly, Is there anyway to doing it quickly, Please help?

Why does my WorkbookView sequence keep returning "null" value with OpenXml?

I have created a download button in C#/asp.net, which takes a GridView, converts it to a data table, and then stores it into a .xlsx file. What I want to do after is be able to change the active tab of the spreadsheet I created.
Here is the code below:
protected void downloadBtn_Click(object sender, EventArgs e)
{
using (var spreadSheet = SpreadsheetDocument.Create(Server.MapPath("~/Downloads/TestSheet.xlsx"), DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook))
{
WorkbookPart workbookPart = spreadSheet.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
//Fine up to this point
spreadSheet.WorkbookPart.Workbook.Sheets = new Sheets();
DataTable table = new DataTable();
//Converts GridView into Data Table **
for (int i = 0; i < gvEmployee.HeaderRow.Cells.Count - 1; i++)
{
table.Columns.Add(gvEmployee.HeaderRow.Cells[i + 1].Text);
}
// fill rows
for (int i = 0; i < gvEmployee.Rows.Count; i++)
{
DataRow dr = table.NewRow();
for (int j = 0; j < gvEmployee.Columns.Count - 1; j++)
{
dr[j] = gvEmployee.Rows[i].Cells[j + 1].Text;
}
table.Rows.Add(dr);
}
var sheetPart = spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
var sheetData = new SheetData();
sheetPart.Worksheet = new Worksheet(sheetData);
Sheets sheets = spreadSheet.WorkbookPart.Workbook.GetFirstChild<Sheets>();
string relationshipId = spreadSheet.WorkbookPart.GetIdOfPart(sheetPart);
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = "TestSheet" };
sheets.Append(sheet);
Row headerRow = new Row();
List<String> columns = new List<string>();
foreach (DataColumn column in table.Columns)
{
columns.Add(column.ColumnName);
Cell cell = new Cell();
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(column.ColumnName);
headerRow.AppendChild(cell);
}
sheetData.AppendChild(headerRow);
foreach (DataRow dsrow in table.Rows)
{
Row newRow = new Row();
foreach (String col in columns)
{
Cell cell = new Cell();
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(dsrow[col].ToString()); //
newRow.AppendChild(cell);
}
sheetData.AppendChild(newRow);
}
Sheet sheet2 = new Sheet() { Id = spreadSheet.WorkbookPart.GetIdOfPart(sheetPart), SheetId = 2, Name = "AdditionalSheet" };
sheets.Append(sheet2);
var sheetIndex = workbookPart.Workbook.Descendants<Sheet>().ToList().IndexOf(sheet2);
WorkbookView workbookView = workbookPart.Workbook.Descendants<WorkbookView>().FirstOrDefault(); //new WorkbookView();
workbookView.ActiveTab = Convert.ToUInt32(sheetIndex);
workbookPart.Workbook.Save();
spreadSheet.Close();
}
}
When I try to run this code, it throws an error on the "workbookView.ActiveTab" at the last few lines stating
System.NullReferenceException: 'Object reference not set to an instance of an object.'
workbookView was null.
Any ideas as to why its doing this?
Okay, I just figured out what was wrong, there was no WorkbookView initialized, as I thought it thought it didn't need to be. But you have to actually make a WorkbookView of your workbook before you can change it. I just needed this one line:
workbookPart.Workbook.Append(new BookViews(new WorkbookView()));

Faster creation of Excel (Interop)

I identified the code slowing down the process as this one (where I'm filling the cells):
What I'm doing here is basically loading some data from a database using a DataSet.
Microsoft.Office.Interop.Excel.Range range1 = null;
Microsoft.Office.Interop.Excel.Range cell1 = null;
Microsoft.Office.Interop.Excel.Borders border1 = null;
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
int s = i + 1;
for (j = 0; j <= ds.Tables[0].Columns.Count - 1; j++)
{
data = ds.Tables[0].Rows[i].ItemArray[j].ToString();
xlWorkSheet.Cells[s + 1, j + 1] = data;
range1 = xlWorkSheet.UsedRange;
cell1 = range1.Cells[s + 1, j + 1];
border1 = cell1.Borders;
if (((IList)terms).Contains(xlWorkSheet.Cells[1, j + 1].Value.ToString()))
{
cell1.Interior.Color = System.Drawing.Color.Red;
}
range1.Columns.AutoFit();
range1.HorizontalAlignment = Microsoft.Office.Interop.Excel.XlHAlign.xlHAlignCenter;
border1.LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlContinuous;
border1.Weight = 2d;
}
}
It's sometimes taking like more than 1 minute to load the whole thing. Is there is away to optimize it?.
Cell-by-cell is the slowest possible way to interact with Excel using Interop - look up how to add data to a sheet from an array in one operation.
E.g.
Write Array to Excel Range
shows this approach.
Interop libraries are extremely slow and spends huge source of system.
Instead of using Interop Libraries to create Excel files, you can simply use it OpenXML library.
I'm using it in production. And over 1 million rows it just takes about 10 seconds to export dataset to excel file.
Here is a sample code quoted from:
Export DataTable to Excel with Open Xml SDK in c#
private void ExportDSToExcel(DataSet ds, string destination)
{
using (var workbook = SpreadsheetDocument.Create(destination, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook))
{
var workbookPart = workbook.AddWorkbookPart();
workbook.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
workbook.WorkbookPart.Workbook.Sheets = new DocumentFormat.OpenXml.Spreadsheet.Sheets();
uint sheetId = 1;
foreach (DataTable table in ds.Tables)
{
var sheetPart = workbook.WorkbookPart.AddNewPart<WorksheetPart>();
var sheetData = new DocumentFormat.OpenXml.Spreadsheet.SheetData();
sheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet(sheetData);
DocumentFormat.OpenXml.Spreadsheet.Sheets sheets = workbook.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>();
string relationshipId = workbook.WorkbookPart.GetIdOfPart(sheetPart);
if (sheets.Elements<DocumentFormat.OpenXml.Spreadsheet.Sheet>().Count() > 0)
{
sheetId =
sheets.Elements<DocumentFormat.OpenXml.Spreadsheet.Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
DocumentFormat.OpenXml.Spreadsheet.Sheet sheet = new DocumentFormat.OpenXml.Spreadsheet.Sheet() { Id = relationshipId, SheetId = sheetId, Name = table.TableName };
sheets.Append(sheet);
DocumentFormat.OpenXml.Spreadsheet.Row headerRow = new DocumentFormat.OpenXml.Spreadsheet.Row();
List<String> columns = new List<string>();
foreach (DataColumn column in table.Columns)
{
columns.Add(column.ColumnName);
DocumentFormat.OpenXml.Spreadsheet.Cell cell = new DocumentFormat.OpenXml.Spreadsheet.Cell();
cell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.String;
cell.CellValue = new DocumentFormat.OpenXml.Spreadsheet.CellValue(column.ColumnName);
headerRow.AppendChild(cell);
}
sheetData.AppendChild(headerRow);
foreach (DataRow dsrow in table.Rows)
{
DocumentFormat.OpenXml.Spreadsheet.Row newRow = new DocumentFormat.OpenXml.Spreadsheet.Row();
foreach (String col in columns)
{
DocumentFormat.OpenXml.Spreadsheet.Cell cell = new DocumentFormat.OpenXml.Spreadsheet.Cell();
cell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.String;
cell.CellValue = new DocumentFormat.OpenXml.Spreadsheet.CellValue(dsrow[col].ToString()); //
newRow.AppendChild(cell);
}
sheetData.AppendChild(newRow);
}
}
}
}

OpenXML Add Cell to WorkSheet

I am currently doing my head in working over the OpenXML 2.5 Framework on the MSDN site here, https://msdn.microsoft.com/en-us/library/office/cc861607.aspx
All methods I have tried to add a cell to an existing worksheet corrupt the workbook as the MSDN site only outlines creating the worksheet and not modifying it.
Everytime I add a cell the system wants a whole new worksheet and will not allow the addition of a cell to an existing worksheet. I have been banging my head for hours going over MSDN and Googling this with no luck.
The problem is I need a class that can receiving strings and update the excel file. Has anyone been able to add a cell to an existing worksheet? My issue seems to be due to a string by string solution.
Working input (PowerShell) only works if a new Worksheet is created for the Cell,
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
$cSharpData = (
[Reflection.Assembly]::LoadWithPartialName("DocumentFormat.OpenXml"),
[Reflection.Assembly]::LoadWithPartialName("WindowsBase"),
[Reflection.Assembly]::LoadWithPartialName("System.Linq")
)
[String]$cSharpClass = Get-Content .\method.cs
$cSharpType = Add-Type -ReferencedAssemblies $cSharpData -TypeDefinition $cSharpClass
$testData = Get-WmiObject Win32_QuickFixEngineering
[DoExcelMethod]::CreateXLSX('.\test.xlsx')
$locNo = 1
[DoExcelMethod]::AddSheetData('.\test.xlsx', $testData, 'TestWS', 'A', $locNo)
The file this is point at has the following,
using System;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
public class DoExcelMethod {
private static int SharedDataItem(string sData, SharedStringTablePart ssPart) {
if (ssPart.SharedStringTable == null) {
ssPart.SharedStringTable = new SharedStringTable();
}
int cnt = 0;
foreach (SharedStringItem sspItem in ssPart.SharedStringTable.Elements<SharedStringItem>()) {
if (sspItem.InnerText == sData) {
return cnt;
}
cnt++;
}
ssPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(sData)));
ssPart.SharedStringTable.Save();
return cnt;
}
private static WorksheetPart InsertWorksheet(string wsName, WorkbookPart wbPart) {
WorksheetPart newWsPart = wbPart.AddNewPart<WorksheetPart>();
newWsPart.Worksheet = new Worksheet(new SheetData());
newWsPart.Worksheet.Save();
Sheets sheets = wbPart.Workbook.GetFirstChild<Sheets>();
string relId = wbPart.GetIdOfPart(newWsPart);
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0) {
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
Sheet sheet = new Sheet() { Id = relId, SheetId = sheetId, Name = wsName };
sheets.Append(sheet);
wbPart.Workbook.Save();
return newWsPart;
}
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart) {
Worksheet worksheet = worksheetPart.Worksheet;
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
string cellReference = columnName + rowIndex;
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 };
sheetData.Append(row);
}
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 {
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);
worksheet.Save();
return newCell;
}
}
public static void CreateXLSX(string xlsxFile) {
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(xlsxFile, SpreadsheetDocumentType.Workbook);
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
Sheet sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Default" };
sheets.Append(sheet);
workbookpart.Workbook.Save();
spreadsheetDocument.Close();
}
public static void AddSheetData(string xlsxFile, string psData, string wsName, string psCol, uint psRow) {
using (SpreadsheetDocument sSheet = SpreadsheetDocument.Open(xlsxFile, true)) {
SharedStringTablePart ssPart;
if (sSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().Count() > 0) {
ssPart = sSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
} else {
ssPart = sSheet.WorkbookPart.AddNewPart<SharedStringTablePart>();
}
int ssIns = SharedDataItem(psData, ssPart);
WorksheetPart wsPart = InsertWorksheet(wsName, sSheet.WorkbookPart);
Cell cell = InsertCellInWorksheet(psCol, psRow, wsPart);
cell.CellValue = new CellValue(ssIns.ToString());
cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
wsPart.Worksheet.Save();
}
}
}
So despite this working I cannot get a cell into an existing worksheet, can anyone help as I am going insane :(
Thanks all
The issue you have is in the call to InsertWorksheet in AddSheetData. You are calling the InsertWorksheet method irrespective of whether or not the worksheet already exists. Instead of doing that, you can first search for the worksheet then if it exists you can use it and if it doesn't you can create a new one.
Firstly, you can search for a WorksheetPart by its name using a method such as this one (taken from my answer here):
private static WorksheetPart GetWorksheetPartBySheetName(WorkbookPart workbookPart, string sheetName)
{
WorksheetPart worksheetPart = null;
//find the sheet (note this is case-sensitive)
IEnumerable<Sheet> sheets = workbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().Where(s => s.Name == sheetName);
if (sheets.Count() > 0)
{
string relationshipId = sheets.First().Id.Value;
worksheetPart = (WorksheetPart)workbookPart.GetPartById(relationshipId);
}
return worksheetPart;
}
If that method finds the WorksheetPart then it will return it, if not it will return null.
Once you have that you just need a small tweak to AddSheetData to call GetWorksheetPartBySheetName then only call InsertWorksheet if that method returns null. To do that you can replace this line
WorksheetPart wsPart = InsertWorksheet(wsName, sSheet.WorkbookPart);
with this
WorksheetPart wsPart = GetWorksheetPartBySheetName(sSheet.WorkbookPart, wsName);
if (wsPart == null)
wsPart = InsertWorksheet(wsName, sSheet.WorkbookPart);

Insert a new worksheet into a spreadsheet document OpenXml

What im trying to do is create multiple worksheets within a work book using datasets the code i have to create a sheet data object from a data set is:
public static SheetData CreateDataSheet(DataSet ds)
{
var xlSheetData = new SheetData();
if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
{
var tbl = ds.Tables[0];
foreach (DataRow row in tbl.Rows)
{
var xlRow = new Row();
foreach (DataColumn col in tbl.Columns)
{
var cellData = row[col];
Cell xlCell = null;
if (cellData != null)
{
xlCell = new Cell(new InlineString(new Text(cellData.ToString())))
{
DataType = CellValues.InlineString
};
}
else
{
xlCell = new Cell(new InlineString(new Text(String.Empty)))
{
DataType = CellValues.InlineString
};
}
xlRow.Append(xlCell);
}
xlSheetData.Append(xlRow);
}
}
return xlSheetData;
}
Then to create the spreadsheet and append the above to the spreadsheet i have:
public static void CreateSpreadsheetWorkbook(string filepath, List<SheetData> sd)
{
var spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook);
var workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
foreach (var x in sd)
{
var newWorksheetPart = spreadsheetDocument.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new Worksheet(x);
var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
uint sheetId = 1;
if (sheets.Elements<Sheet>().Any())
{
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
var sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(newWorksheetPart), SheetId = sheetId, Name = "mySheet" + sheetId };
sheets.Append(sheet);
workbookpart.Workbook.Save();
}
spreadsheetDocument.Close();
}
This runs with out any errors however when i come to open the document its unable to be opened because it is corrupt.
EDIT - final working version:
public static void CreateSpreadsheetWorkbook(string filepath, List<SheetData> sd)
{
var spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook);
var workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
foreach (var x in sd)
{
var newWorksheetPart = spreadsheetDocument.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new Worksheet(x);
sheets = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>();
uint sheetId = 1;
if (sheets.Elements<Sheet>().Any())
{
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
var sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(newWorksheetPart), SheetId = sheetId, Name = "mySheet" + sheetId };
sheets.Append(sheet);
workbookpart.Workbook.Save();
}
spreadsheetDocument.Close();
}
After comparing with some similar code of mine I found these possible problems:
var spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook);
//should be
var spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.DocumentType);
And
var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
//should be
var sheets = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>();
after the first time since by the second time you already have a Sheets element.
See this blogpost for more details:
http://blogs.msdn.com/b/brian_jones/archive/2009/02/19/how-to-copy-a-worksheet-within-a-workbook.aspx

Categories