I use the following code
// Create a spreadsheet document
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(excelLocation + #"\example1.xlsx", 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());
UInt32Value sheetCount = 0;
foreach (string value in Holder.country)
{
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() {
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = sheetCount,
Name = value
};
sheets.Append(sheet);
sheetCount++;
}
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
But it always told me
Excel found unreadable content in"example1.xlsx". Do you want to recover the contents of this workbook? ...
I read something on the internet and they say it's due to the sheetCount. How possible is it in this as I had add 1 every loop.
Some confusion here.
You need to add a worksheetpart for each sheet you create. Here, you
refer at only one worksheetpart for all sheets.
The minimum value for sheetId is 1, not 0.
// Create a spreadsheet document
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(excelLocation + #"\example1.xlsx", SpreadsheetDocumentType.Workbook);
// Add a WorkbookPart to the document
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
UInt32Value sheetCount = 1; // Mimimum value is 1
foreach (string value in Holder.country)
{
// Add a WorksheetPart to the WorkbookPart
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() {
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = sheetCount,
Name = value
};
sheets.Append(sheet);
sheetCount++;
}
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
Related
There is code which works with Excel like this:
using SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open("filePath", true);
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart!;
SheetData sheetData = GetSheetData(workbookPart, outFile);
Worksheet worksheet = sheetData.Parent as Worksheet;
List<SharedStringItem> sharedStringTableElements = workbookPart.SharedStringTablePart!.SharedStringTable.Elements<SharedStringItem>().ToList();
List<Row> sheetRows = sheetData.Elements<Row>().ToList();
...
But it breaks when I try to open my generated file. SharedStringTablePart is null.
This is how I create Excel files:
using SpreadsheetDocument spreadSheet = CreateSpreadsheet(filePath);
WorkbookPart workbookPart = spreadSheet.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
SheetData sheetData = new();
worksheetPart.Worksheet = new Worksheet(sheetData);
Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
string relationshipId = workbookPart.GetIdOfPart(worksheetPart);
Sheet sheet = new() {
Id = relationshipId,
SheetId = FirstSheetId,
Name = "SheetName",
};
sheets.Append(sheet);
//fill sheetData here
workbookPart.Workbook.Save();
spreadSheet.Close();
How do I change this code so it doesn't break when accessing a SharedStringTablePart? How can I fill it in correctly?
Thanks in advance.
You create a new SpreadsheetDocument and add the WorkbookPart. Right after being created, that new WorkbookPart does not have any associated parts. Thus, you'll have to create all required parts, including the SharedStringTablePart, like so:
// Let's assume you created the WorkbookPart as follows.
WorkbookPart workbookPart = spreadSheet.AddWorkbookPart();
// Create and initialize the associated SharedStringTablePart as well.
var sharedStringTablePart = workbookPart.AddNewPart<SharedStringTablePart>();
sharedStringTablePart.SharedStringTable = new SharedStringTable();
Once added as shown above, workbookPart.SharedStringTablePart will no longer be null.
I came back to write how I did it.
First of all, we need to create SharedStringTablePart as Thomas said:
private static SharedStringTablePart GetSharedStringTablePart(SpreadsheetDocument spreadSheet)
{
if (spreadSheet.WorkbookPart!.GetPartsOfType<SharedStringTablePart>().Any())
{
return spreadSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
}
else
{
return spreadSheet.WorkbookPart.AddNewPart<SharedStringTablePart>();
}
}
Then I created a helper method to insert the data into this table:
private static string InsertSharedStringItem(string text, SharedStringTablePart shareStringPart)
{
shareStringPart.SharedStringTable ??= new SharedStringTable();
int i = 0;
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>())
{
if (item.InnerText == text)
{
return i.ToString();
}
i++;
}
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(text)));
shareStringPart.SharedStringTable.Save();
return i.ToString();
}
So next time when I inserting cell value I do it like this:
string index = InsertSharedStringItem("some text", sharedTablePart);
Cell cell = new() {
DataType = CellValues.SharedString,
CellValue = new CellValue(index),
...
};
row.AppendChild(cell);
I'm using Documentformat.Openxml package
[https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet?view=openxml-2.8.1]
to generate a Pivot table in a CSV dynamically from a Dictionary<string, List<string>> object which has a structure similar to this:
I'm following the Sample here to create the table which should look somewhat like:
but not sure about how to create the table from the dictionary of data.
My current code snippet:
// 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(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "mySheet"
};
sheets.Append(sheet);
Worksheet worksheet = new Worksheet();
SheetData sheetData = new SheetData();
Row row = new Row();
Cell cell = new Cell()
{
CellReference = "A1",
DataType = CellValues.String,
CellValue = new CellValue("Microsoft")
};
row.Append(cell);
sheetData.Append(row);
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
// Close the document.
spreadsheetDocument.Close();
In the above snippet, you can see that I'm hardcoding the cell value but in reality I want to populate it dynamically from the dictionary and there is a large amount of data in it. What is the best way to do this? Can someone please help?
As your question shows you want to generate excel file using Open XML. You should take a look at these answers
1 Using open XML to create excel file
2 How do you convert Excel to CSV using OpenXML SDK?
and for other websites you can visit
3 https://www.c-sharpcorner.com/article/export-and-import-excel-file-using-closedxml-in-asp-net-mvc/
I want to display number 3123.45 as 3,123.45 by using C# as follow coding. I tried many codes but didn't find good example and code.
public static void CreateSpreadsheetWorkbook(string filepath)
{
// 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 sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "mySheet" };
sheets.Append(sheet);
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
}
You need to set the style for the selected column that you want:
sheet.Column(columnIndex).Style.Numberformat.Format = "#,##0.00";
I think you can also do this for cells.
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();
}
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.