Programmatically sort Excel spreadsheets - c#

I am using OpenXML to manipulate Excel files.
I am sending the Excel files as memory stream, editing them and then send them back to browser so they open in client office program. I create new spread sheet by using this code:
public static void InsertWorksheet(string docName)
{
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(docName, true))
{
// Add a blank WorksheetPart.
WorksheetPart newWorksheetPart = spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new Worksheet(new SheetData());
Sheets sheets = spreadSheet.WorkbookPart.Workbook.GetFirstChild<Sheets>();
string relationshipId = spreadSheet.WorkbookPart.GetIdOfPart(newWorksheetPart);
// Get a unique ID for the new worksheet.
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
// Give the new worksheet a name.
string sheetName = "Sheet" + sheetId;
// Append the new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = sheetName };
sheets.Append(sheet);
}
}
My problem is that this sheet is added last among the sheets in the workbook. I want it to be sheet nr 1. I am looking for tips on how I can set my newly created sheet to be sheet nr. 1.

Maybe instead of sheets.Append you call something that inserts it at the beginning? is there a Insert or Prepend method?

Related

OpenXML - How can I insert a value into a specific worksheet?

I'm fairly new to creating Excel spreadsheets in C# and I'm looking for advice.
I've spent 2 or 3 days now looking through documentation and blogs etc but I cannot seem to find an answer to a common task.
I need to insert a text value into a specific worksheet. I cant easily post my code at the moment but it appears to be an issue with every example I've seen of the definition of a sheet, it always gets the first child.
I need to iterate through all sheets and dependant on the sheet name then go and insert a value.
I.e. If the sheet name = "testA" then write TestA, if the sheet name = "testB" then write "TestB".
Currently I can insert a value for the workbook but it inserts the same value for every sheet.
Sheets sheets = workbookPart.Workbook.GetFirstChild<Sheets>();
[Microsoft documentation] (https://learn.microsoft.com/en-us/office/open-xml/how-to-insert-text-into-a-cell-in-a-spreadsheet)
Please note I'm not just giving up on this I've just reached a bit of a wall with it and need some pointers.
Many Thanks, J
Namespaces I'm using:
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
private void btnExport_Click(object sender, EventArgs e)
{
//Using Microsofts Interop class to create Excel files gives mixed resuts and requires excel to be installed.
// This uses OpenXML library to achieve this and doesnt require excel to be installed: https://medium.com/swlh/openxml-sdk-brief-guide-c-7099e2391059
string filepath = "Test.xlsx";
// 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.
Sheet sheetOne = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet1"
};
Sheet sheetTwo= new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 2,
Name = "Sheet2"
};
Sheet sheetParameters = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 3,
Name = "Parameters"
};
Sheet sheetFour= new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 4,
Name = "Sheet4"
};
Sheet sheetFive= new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 5,
Name = "Sheet5"
};
sheets.Append(sheetOne);
sheets.Append(sheetTwo);
sheets.Append(sheetParameters);
sheets.Append(sheetFour);
sheets.Append(sheetFive);
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
//InsertTextExistingExcel(filepath, "test1", "A", 1, "Parameters");
InsertInSheet(filepath, "test1", "A", 1, "Parameters");
//Any of these lists empty?
}
public static void InsertInSheet(string filePath, string value, string columnName, uint rowIndex, string sheetName)
{
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(filePath, true))
{
getSheetDetails(spreadSheet);
}
}
//work here
public static void getSheetDetails(SpreadsheetDocument doc)
{
WorksheetPart worksheetPart;
//iterate through sheets
foreach (Sheet sheetDetail in doc.WorkbookPart.Workbook.Sheets)
{
if (sheetDetail.Name == "Parameters")
{
//each sheet has a worksheetPart
worksheetPart = (WorksheetPart)doc.WorkbookPart.GetPartById(sheetDetail.Id);
Cell cell = InsertCellInWorksheet("A", 1, worksheetPart);
cell.CellValue = new CellValue("test");
cell.DataType = new EnumValue<CellValues>(CellValues.String);
worksheetPart.Worksheet.Save();
}
}
}
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart)
{
//fix earlier worksheetPart only has a count of 1
Worksheet worksheet = worksheetPart.Worksheet;
//fix this to point to sheet
//SheetData sheetData = worksheet.Descendants<SheetData>();
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
MessageBox.Show(sheetData.InnerXml.ToString());
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);
}
Cell refCell = row.Descendants<Cell>().LastOrDefault();
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertAfter(newCell, refCell);
worksheet.Save();
return newCell;
}

Simple excel creation does not open file is corrupt

I am trying to create a simple excel file with multiple sheets using open xml, unfortunately the file does not open after it's being created.
After the file is generated, when I open it with Microsoft Excel it says
We found a problem, do you want to recover as much as we can?
using (SpreadsheetDocument spreedDoc = SpreadsheetDocument.Create(filePath,
DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook))
{
WorkbookPart wbPart = spreedDoc.WorkbookPart;
wbPart = spreedDoc.AddWorkbookPart();
wbPart.Workbook = new Workbook();
Sheets sheets = wbPart.Workbook.AppendChild(new Sheets());
foreach (var sheetData in excelSheetData)
{
// Add a blank WorksheetPart.
WorksheetPart worksheetPart = wbPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
string relationshipId = wbPart.GetIdOfPart(worksheetPart);
// Get a unique ID for the new worksheet.
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
// Give the new worksheet a name.
string sheetNameToWrite = sheetName;
if (string.IsNullOrWhiteSpace(sheetNameToWrite))
{
sheetNameToWrite = "Sheet"+sheetId;
}
// Append the new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = sheetName };
sheets.AppendChild(sheet);
}
//wbPart.Workbook.Sheets.AppendChild(sheet);
wbPart.Workbook.Save();
}
On trying to Repair in excel gives below message
-<repairedRecords summary="Following is a list of repairs:">
<repairedRecord>Repaired Records: Worksheet properties from /xl/workbook.xml part (Workbook)</repairedRecord>
</repairedRecords>
</recoveryLog>
Have you seen this?
http://www.mikesknowledgebase.com/pages/CSharp/ExportToExcel.htm
The necessary steps to create a functional excel file with multiple worksheets in OpenXML (that work for me) are as follows:
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook))
{
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 = stylesheet;
workbookStylesPart.Stylesheet.Save();
for (int worksheetNo = 1; worksheetNo < worksheetCountYouWantToCreate; worksheetNo++)
{
string workSheetID = "rId" + worksheetNo;
string worksheetName = "worksheet" + worksheetNo;
WorksheetPart newWorksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet();
newWorksheetPart.Worksheet.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.SheetData());
// write data here
// ...
// ...
newWorksheetPart.Worksheet.Save();
if (worksheetNo == 1)
spreadsheet.WorkbookPart.Workbook.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheets());
spreadsheet.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>().AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheet()
{
Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart),
SheetId = (uint)worksheetNo,
Name = worksheetName
});
}
spreadsheet.WorkbookPart.Workbook.Save();
}

The process cannot access the file because it is being used by another process using spreadsheet document

Please help me to solve the problem, when i save the excel file throw this error i convert the excel file to zip file.
zip.Save(#"" + root + "/" + "" + userid + ".zip");
the process cannot access the file because it is being used by another process
my code is below
using (SpreadsheetDocument document = SpreadsheetDocument.Create(excelFilename, SpreadsheetDocumentType.Workbook))
{
WriteExcelFile(ds, document);
}
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()));
// If we don't add a "WorkbookStylesPart", OLEDB will refuse to connect to this .xlsx file !
WorkbookStylesPart workbookStylesPart = spreadsheet.WorkbookPart.AddNewPart<WorkbookStylesPart>("rIdStyles");
Stylesheet stylesheet = new Stylesheet();
workbookStylesPart.Stylesheet = stylesheet;
// Loop through each of the DataTables in our DataSet, and create a new Excel Worksheet for each.
uint worksheetNumber = 1;
foreach (DataTable dt in ds.Tables)
{
// For each worksheet you want to create
string workSheetID = "rId" + worksheetNumber.ToString();
string worksheetName = dt.TableName;
WorksheetPart newWorksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet();
// create sheet data
newWorksheetPart.Worksheet.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.SheetData());
// save worksheet
WriteDataTableToExcelWorksheet(dt, newWorksheetPart);
newWorksheetPart.Worksheet.Save();
// create the worksheet to workbook relation
if (worksheetNumber == 1)
spreadsheet.WorkbookPart.Workbook.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheets());
spreadsheet.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>().AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheet()
{
Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart),
SheetId = (uint)worksheetNumber,
Name = dt.TableName
});
worksheetNumber++;
}
spreadsheet.WorkbookPart.Workbook.Save();
}
I see that you have saved your document but you have not closed it. So, it will still be open with the spreadsheetdocument object.
Add the following line after you save the document.
spreadsheet.WorkbookPart.Workbook.Close();
If it doesn't help, kindly provide the full code so that we can look into it further. Good luck.
I think you are not closing all your Workbooks before you try to put them in the Zip-File.
Put this at the end of the Method:
private static void WriteExcelFile(DataSet ds, SpreadsheetDocument spreadsheet)
{
...
spreadsheet.WorkbookPart.Workbook.Close();
}

Exporting to Excel (OpenXML) results in Unreadable Content

I've been struggeling with creating worksheets for my xlsx file. Adding the first worksheet isn't a problem, but when I want a second sheet, the exported xlsx seems to be corrupt. Who can point out to me what I'm doing wrong?
Note: I already tried to also call 'workbookpart,Workbook.Save();' right after creating the first Workbook, but without the required result.
protected void export_Click(object sender, EventArgs e)
{
ExportToExcel(#"D:\dev\Dotnet4\Excel\test.xlsx");
}
private void ExportToExcel(string filepath)
{
SpreadsheetDocument spreadsheetDocument;
WorkbookPart workbookpart;
CreateSpreadsheet(filepath, out spreadsheetDocument, out workbookpart);
CreateWorksheet(spreadsheetDocument, workbookpart, "My sheet 1");
CreateWorksheet(spreadsheetDocument, workbookpart, "My sheet 2");
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
}
private static void CreateWorksheet(SpreadsheetDocument spreadsheetDocument, WorkbookPart workbookpart, string worksheetName)
{
// 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.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 1,
Name = worksheetName
};
sheets.Append(sheet);
}
private static void CreateSpreadsheet(string filepath, out SpreadsheetDocument spreadsheetDocument, out WorkbookPart workbookpart)
{
// Create a spreadsheet document by supplying the filepath.
// By default, AutoSave = true, Editable = true, and Type = xlsx.
spreadsheetDocument = SpreadsheetDocument.
Create(filepath, SpreadsheetDocumentType.Workbook);
// Add a WorkbookPart to the document.
workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
}
I think you have 2 small issues:
The SheetId is the same for both sheets you are adding which is invalid.
You are adding a new Sheets element each time you call CreateWorksheet but there should only be one such element in the XML (there are many Sheet's but not many Sheets!).
To solve 1 you could use the count of the sheets.ChildElements. As this is 0 first time round and one the second you'll need to add 1 to it. If you prefer you could take it in to CreateWorksheet as a parameter; it doesn't really matter as long as they start at 1 and are different.
To solve 2 you can perform a null check on the Sheets property of the Workbook and only create it if it doesn't already exist.
The below should do what you're after.
private static void CreateWorksheet(SpreadsheetDocument spreadsheetDocument, WorkbookPart workbookpart, string worksheetName)
{
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Add Sheets to the Workbook.
if (spreadsheetDocument.WorkbookPart.Workbook.Sheets == null)
{
//spreadsheetDocument.WorkbookPart.Workbook.Sheets = new Sheets();
spreadsheetDocument.WorkbookPart.Workbook
.AppendChild<Sheets>(new Sheets());
}
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.Sheets;
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = (UInt32)sheets.ChildElements.Count + 1,
Name = worksheetName
};
sheets.Append(sheet);
}
One final point is that SpreadsheetDocument implements IDisposable so it should be disposed after use.

Inserting WorkSheet with Open XML - "...unreadable content..."

When I follow this tutorial:
http://msdn.microsoft.com/en-us/library/cc881781.aspx
to open an Excel document and insert an empty worksheet the final result is a message telling "Excel found unreadable content in ... Do you want to recover the contents of this workbook...". If I recover all the inserted sheets are blank (even if I add content programatically)
After renaming the xlsx to .zip and examining it shows that the worksheets have been created, and content added.
Anyone with any similar problems? It may be something with not creating relationships between newly created parts...
That error message means that the XML that makes up your excel document is not conforming to the XML Schema and is invalid. You can use the Open XML SDK 2.0 Productivity Tool to see where the issue is located.
I also copied the code from the bottom of your link and got it to work like Chris said in his comment. Does your code look like this below?
// Open the document for editing.
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(docName, true))
{
// Add a blank WorksheetPart.
WorksheetPart newWorksheetPart =
spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new Worksheet(new SheetData());
Sheets sheets = spreadSheet.WorkbookPart.Workbook.GetFirstChild<Sheets>();
string relationshipId =
spreadSheet.WorkbookPart.GetIdOfPart(newWorksheetPart);
// Get a unique ID for the new worksheet.
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId =
sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
// Give the new worksheet a name.
string sheetName = "Sheet" + sheetId;
// Append the new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{ Id = relationshipId, SheetId = sheetId, Name = sheetName };
sheets.Append(sheet);
string docName = #"C:\Users\Public\Documents\Sheet7.xlsx";
InsertWorksheet(docName);
}
// Given a document name, inserts a new worksheet.
public static void InsertWorksheet(string docName)
{
// Open the document for editing.
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(docName, true))
{
// Add a blank WorksheetPart.
WorksheetPart newWorksheetPart = spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new Worksheet(new SheetData());
Sheets sheets = spreadSheet.WorkbookPart.Workbook.GetFirstChild<Sheets>();
string relationshipId = spreadSheet.WorkbookPart.GetIdOfPart(newWorksheetPart);
// Get a unique ID for the new worksheet.
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
// Give the new worksheet a name.
string sheetName = "Sheet" + sheetId;
// Append the new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = sheetName };
sheets.Append(sheet);
}
}

Categories